Skip to content

Commit

Permalink
Silent Payments (#197)
Browse files Browse the repository at this point in the history
  • Loading branch information
csjones authored Jun 3, 2022
1 parent c2740e2 commit 1a796f7
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 55 deletions.
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,33 @@ let tweakedPublicKeyKey = try! privateKey.publicKey.tweak(tweak)
## Elliptic Curve Diffie Hellman

```swift
let privateKey1 = try! secp256k1.KeyAgreement.PrivateKey()
let privateKey2 = try! secp256k1.KeyAgreement.PrivateKey()
let privateKey = try! secp256k1.KeyAgreement.PrivateKey()
let publicKey = try! secp256k1.KeyAgreement.PrivateKey().publicKey

// Create a shared secret with a private key from only a public key
let sharedSecret = try! privateKey.sharedSecretFromKeyAgreement(with: publicKey)
```

## Silent Payment

```swift
let privateSign1 = try! secp256k1.Signing.PrivateKey()
let privateSign2 = try! secp256k1.Signing.PrivateKey()

let privateKey1 = try! secp256k1.KeyAgreement.PrivateKey(rawRepresentation: privateSign1.rawRepresentation)
let privateKey2 = try! secp256k1.KeyAgreement.PrivateKey(rawRepresentation: privateSign2.rawRepresentation)

let sharedSecret1 = try! privateKey1.sharedSecretFromKeyAgreement(with: privateKey2.publicKey)
let sharedSecret2 = try! privateKey2.sharedSecretFromKeyAgreement(with: privateKey1.publicKey)

let sharedSecretSign1 = try! secp256k1.Signing.PrivateKey(rawRepresentation: sharedSecret1.bytes)
let sharedSecretSign2 = try! secp256k1.Signing.PrivateKey(rawRepresentation: sharedSecret2.bytes)

// Payable Silent Payment public key
let xonlyTweak2 = try! sharedSecretSign2.publicKey.xonly.add(privateSign1.publicKey.xonly.bytes)

// Spendable Silent Payment private key
let privateTweak1 = try! sharedSecretSign1.add(xonly: privateSign1.publicKey.xonly.bytes)
```


Expand Down
16 changes: 11 additions & 5 deletions Sources/implementation/Asymmetric.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public extension secp256k1 {
private let baseKey: PublicKeyImplementation

/// The secp256k1 public key object
var keyBytes: [UInt8] {
var bytes: [UInt8] {
baseKey.bytes
}

Expand Down Expand Up @@ -109,8 +109,8 @@ public extension secp256k1 {
/// Generates a secp256k1 public key from a raw representation.
/// - Parameter data: A raw representation of the key.
/// - Throws: An error is thrown when the raw representation does not create a public key.
public init<D: ContiguousBytes>(rawRepresentation data: D, xonly: D, format: secp256k1.Format) {
self.baseKey = PublicKeyImplementation(rawRepresentation: data, xonly: xonly, format: format)
public init<D: ContiguousBytes>(rawRepresentation data: D, xonly: D, keyParity: Int32, format: secp256k1.Format) {
self.baseKey = PublicKeyImplementation(rawRepresentation: data, xonly: xonly, keyParity: keyParity, format: format)
}
}

Expand All @@ -124,12 +124,18 @@ public extension secp256k1 {
baseKey.bytes
}

/// A boolean that will be set to true if the point encoded by xonly is the
/// negation of the pubkey and set to false otherwise.
public var parity: Bool {
baseKey.keyParity.boolValue
}

fileprivate init(baseKey: XonlyKeyImplementation) {
self.baseKey = baseKey
}

public init<D: ContiguousBytes>(rawRepresentation data: D) {
self.baseKey = XonlyKeyImplementation(rawRepresentation: data)
public init<D: ContiguousBytes>(rawRepresentation data: D, keyParity: Int32) {
self.baseKey = XonlyKeyImplementation(rawRepresentation: data, keyParity: keyParity)
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/implementation/ECDH.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ public extension secp256k1 {
/// - data: A raw representation of the public key as a collection of contiguous bytes.
/// - xonly: A raw representation of the xonly key as a collection of contiguous bytes.
/// - format: the format of the public key object
public init<D: ContiguousBytes>(rawRepresentation data: D, xonly: D, format: secp256k1.Format) {
self.baseKey = PublicKeyImplementation(rawRepresentation: data, xonly: xonly, format: format)
public init<D: ContiguousBytes>(rawRepresentation data: D, xonly: D, keyParity: Int32, format: secp256k1.Format) {
self.baseKey = PublicKeyImplementation(rawRepresentation: data, xonly: xonly, keyParity: keyParity, format: format)
}

/// Initializes a secp256k1 public key for key agreement.
Expand Down
105 changes: 93 additions & 12 deletions Sources/implementation/Tweak.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,46 @@ public extension secp256k1.Signing.PrivateKey {
/// Create a new `PrivateKey` by adding tweak to the secret key.
/// - Parameter tweak: the 32-byte tweak object
/// - Returns: tweaked `PrivateKey` object
func tweak(_ tweak: [UInt8]) throws -> Self {
func add(_ tweak: [UInt8]) throws -> Self {
var privateBytes = key.bytes

guard secp256k1_ec_seckey_tweak_add(secp256k1.Context.raw, &privateBytes, tweak).boolValue,
secp256k1_ec_seckey_verify(secp256k1.Context.raw, privateBytes).boolValue else {
throw secp256k1Error.underlyingCryptoError
}

return try Self(rawRepresentation: privateBytes)
}

/// Create a new `PrivateKey` by adding tweak to the secret key. When tweaking x-only keys,
/// the implicit negations are handled when odd Y coordinates are reached.
/// [REF](https://github.com/bitcoin-core/secp256k1/issues/1021#issuecomment-983021759)
/// - Parameter tweak: the 32-byte tweak object
/// - Returns: tweaked `PrivateKey` object
func add(xonly tweak: [UInt8]) throws -> Self {
var keypair = secp256k1_keypair()
var privateBytes = [UInt8](repeating: 0, count: secp256k1.ByteDetails.count)
var xonly = secp256k1_xonly_pubkey()
var keyParity = Int32()

guard secp256k1_keypair_create(secp256k1.Context.raw, &keypair, key.bytes).boolValue,
secp256k1_keypair_xonly_tweak_add(secp256k1.Context.raw, &keypair, tweak).boolValue,
secp256k1_keypair_sec(secp256k1.Context.raw, &privateBytes, &keypair).boolValue else {
secp256k1_keypair_sec(secp256k1.Context.raw, &privateBytes, &keypair).boolValue,
secp256k1_keypair_xonly_pub(secp256k1.Context.raw, &xonly, &keyParity, &keypair).boolValue else {
throw secp256k1Error.underlyingCryptoError
}

return try Self(rawRepresentation: privateBytes)
}

/// Create a new `PrivateKey` by multiplying tweak to the secret key.
/// - Parameter tweak: the 32-byte tweak object
/// - Returns: tweaked `PrivateKey` object
func multiply(_ tweak: [UInt8]) throws -> Self {
var privateBytes = key.bytes

guard secp256k1_ec_seckey_tweak_mul(secp256k1.Context.raw, &privateBytes, tweak).boolValue,
secp256k1_ec_seckey_verify(secp256k1.Context.raw, privateBytes).boolValue else {
throw secp256k1Error.underlyingCryptoError
}

Expand All @@ -35,23 +68,71 @@ public extension secp256k1.Signing.PublicKey {
/// - tweak: the 32-byte tweak object
/// - format: the format of the tweaked `PublicKey` object
/// - Returns: tweaked `PublicKey` object
func tweak(_ tweak: [UInt8], format: secp256k1.Format = .compressed) throws -> Self {
var xonlyPubKey = secp256k1_xonly_pubkey()
func add(_ tweak: [UInt8], format: secp256k1.Format = .compressed) throws -> Self {
var pubKey = secp256k1_pubkey()
var pubKeyLen = format.length
var pubKeyBytes = [UInt8](repeating: 0, count: pubKeyLen)
var xonlyKey = secp256k1_xonly_pubkey()
var xonlyBytes = [UInt8](repeating: 0, count: secp256k1.Schnorr.xonlyByteCount)
var keyParity = Int32()

guard secp256k1_ec_pubkey_parse(secp256k1.Context.raw, &pubKey, bytes, pubKeyLen).boolValue,
secp256k1_ec_pubkey_tweak_add(secp256k1.Context.raw, &pubKey, tweak).boolValue,
secp256k1_ec_pubkey_serialize(secp256k1.Context.raw, &pubKeyBytes, &pubKeyLen, &pubKey, format.rawValue).boolValue,
secp256k1_xonly_pubkey_from_pubkey(secp256k1.Context.raw, &xonlyKey, &keyParity, &pubKey).boolValue,
secp256k1_xonly_pubkey_serialize(secp256k1.Context.raw, &xonlyBytes, &xonlyKey).boolValue else {
throw secp256k1Error.underlyingCryptoError
}

return Self(rawRepresentation: pubKeyBytes, xonly: xonlyBytes, keyParity: keyParity, format: format)
}

/// Create a new `PublicKey` by multiplying tweak to the public key.
/// - Parameters:
/// - tweak: the 32-byte tweak object
/// - format: the format of the tweaked `PublicKey` object
/// - Returns: tweaked `PublicKey` object
func multiply(_ tweak: [UInt8], format: secp256k1.Format = .compressed) throws -> Self {
var pubKey = secp256k1_pubkey()
var pubKeyLen = format.length
var pubBytes = [UInt8](repeating: 0, count: pubKeyLen)
var xonlyPubKeyOutput = secp256k1_xonly_pubkey()
var pubKeyBytes = [UInt8](repeating: 0, count: pubKeyLen)
var xonlyKey = secp256k1_xonly_pubkey()
var xonlyBytes = [UInt8](repeating: 0, count: secp256k1.Schnorr.xonlyByteCount)
var keyParity = Int32()

guard secp256k1_ec_pubkey_parse(secp256k1.Context.raw, &pubKey, bytes, pubKeyLen).boolValue,
secp256k1_ec_pubkey_tweak_mul(secp256k1.Context.raw, &pubKey, tweak).boolValue,
secp256k1_ec_pubkey_serialize(secp256k1.Context.raw, &pubKeyBytes, &pubKeyLen, &pubKey, format.rawValue).boolValue,
secp256k1_xonly_pubkey_from_pubkey(secp256k1.Context.raw, &xonlyKey, &keyParity, &pubKey).boolValue,
secp256k1_xonly_pubkey_serialize(secp256k1.Context.raw, &xonlyBytes, &xonlyKey).boolValue else {
throw secp256k1Error.underlyingCryptoError
}

return Self(rawRepresentation: pubKeyBytes, xonly: xonlyBytes, keyParity: keyParity, format: format)
}
}

public extension secp256k1.Signing.XonlyKey {
/// Create a new `XonlyKey` by adding tweak to the x-only public key.
/// - Parameters:
/// - tweak: the 32-byte tweak object
/// - format: the format of the tweaked `XonlyKey` object
/// - Returns: tweaked `PublicKey` object
func add(_ tweak: [UInt8]) throws -> Self {
var pubKey = secp256k1_pubkey()
var inXonlyPubKey = secp256k1_xonly_pubkey()
var outXonlyPubKey = secp256k1_xonly_pubkey()
var xonlyBytes = [UInt8](repeating: 0, count: secp256k1.Schnorr.xonlyByteCount)
var keyParity = Int32()

guard secp256k1_xonly_pubkey_parse(secp256k1.Context.raw, &xonlyPubKey, xonly.bytes).boolValue,
secp256k1_xonly_pubkey_tweak_add(secp256k1.Context.raw, &pubKey, &xonlyPubKey, tweak).boolValue,
secp256k1_ec_pubkey_serialize(secp256k1.Context.raw, &pubBytes, &pubKeyLen, &pubKey, format.rawValue).boolValue,
secp256k1_xonly_pubkey_from_pubkey(secp256k1.Context.raw, &xonlyPubKeyOutput, &keyParity, &pubKey).boolValue,
secp256k1_xonly_pubkey_serialize(secp256k1.Context.raw, &xonlyBytes, &xonlyPubKeyOutput).boolValue else {
guard secp256k1_xonly_pubkey_parse(secp256k1.Context.raw, &inXonlyPubKey, bytes).boolValue,
secp256k1_xonly_pubkey_tweak_add(secp256k1.Context.raw, &pubKey, &inXonlyPubKey, tweak).boolValue,
secp256k1_xonly_pubkey_from_pubkey(secp256k1.Context.raw, &outXonlyPubKey, &keyParity, &pubKey).boolValue,
secp256k1_xonly_pubkey_serialize(secp256k1.Context.raw, &xonlyBytes, &outXonlyPubKey).boolValue,
secp256k1_xonly_pubkey_tweak_add_check(secp256k1.Context.raw, &xonlyBytes, keyParity, &inXonlyPubKey, tweak).boolValue else {
throw secp256k1Error.underlyingCryptoError
}

return Self(rawRepresentation: pubBytes, xonly: xonlyBytes, format: format)
return Self(rawRepresentation: xonlyBytes, keyParity: keyParity)
}
}
Loading

0 comments on commit 1a796f7

Please sign in to comment.