From 050ff400b7b615d0fca4f007a7ff428a3ca03b6f Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 6 Mar 2018 18:06:09 -0500 Subject: [PATCH 01/45] Rearrange shutdown logic to remove some lock switching (#4083) * Rearrange shutdown logic to remove some lock switching and to allow giving up the HA lock to be skipped. --- vault/core.go | 155 +++++++++++++++++++---------- vault/logical_system_integ_test.go | 21 ++-- 2 files changed, 112 insertions(+), 64 deletions(-) diff --git a/vault/core.go b/vault/core.go index ef43d4dc65e3..535c71e65479 100644 --- a/vault/core.go +++ b/vault/core.go @@ -110,6 +110,10 @@ var ( LastRemoteWAL = lastRemoteWALImpl ) +type StopOptions struct { + KeepLock bool +} + // NonFatalError is an error that can be returned during NewCore that should be // displayed but not cause a program exit type NonFatalError struct { @@ -190,10 +194,12 @@ type Core struct { stateLock sync.RWMutex sealed bool - standby bool - standbyDoneCh chan struct{} - standbyStopCh chan struct{} - manualStepDownCh chan struct{} + standby bool + standbyDoneCh chan struct{} + lockAcquisitionStopCh chan struct{} + standbyStopCh chan StopOptions + manualStepDownCh chan struct{} + heldHALock physical.Lock // unlockInfo has the keys provided to Unseal until the threshold number of parts is available, as well as the operation nonce unlockInfo *unlockInformation @@ -626,6 +632,7 @@ func NewCore(conf *CoreConfig) (*Core, error) { // problem. It is only used to gracefully quit in the case of HA so that failover // happens as quickly as possible. func (c *Core) Shutdown() error { + c.logger.Trace("core: shutdown called") c.stateLock.RLock() // Tell any requests that know about this to stop if c.activeContextCancelFunc != nil { @@ -633,15 +640,13 @@ func (c *Core) Shutdown() error { } c.stateLock.RUnlock() + c.logger.Trace("core: shutdown initiating internal seal") // Seal the Vault, causes a leader stepdown - retChan := make(chan error) - go func() { - c.stateLock.Lock() - defer c.stateLock.Unlock() - retChan <- c.sealInternal() - }() + c.stateLock.Lock() + defer c.stateLock.Unlock() - return <-retChan + c.logger.Trace("core: shutdown running internal seal") + return c.sealInternal(false) } // CORSConfig returns the current CORS configuration @@ -1237,9 +1242,10 @@ func (c *Core) unsealInternal(ctx context.Context, masterKey []byte) (bool, erro } else { // Go to standby mode, wait until we are active to unseal c.standbyDoneCh = make(chan struct{}) - c.standbyStopCh = make(chan struct{}) c.manualStepDownCh = make(chan struct{}) - go c.runStandby(c.standbyDoneCh, c.standbyStopCh, c.manualStepDownCh) + c.lockAcquisitionStopCh = make(chan struct{}) + c.standbyStopCh = make(chan StopOptions) + go c.runStandby(c.standbyDoneCh, c.manualStepDownCh, c.lockAcquisitionStopCh, c.standbyStopCh) } // Success! @@ -1406,19 +1412,15 @@ func (c *Core) sealInitCommon(ctx context.Context, req *logical.Request) (retErr c.stateLock.RUnlock() //Seal the Vault - retChan := make(chan error) - go func() { - c.stateLock.Lock() - defer c.stateLock.Unlock() - retChan <- c.sealInternal() - }() + c.stateLock.Lock() + defer c.stateLock.Unlock() + sealErr := c.sealInternal(false) - funcErr := <-retChan - if funcErr != nil { - retErr = multierror.Append(retErr, funcErr) + if sealErr != nil { + retErr = multierror.Append(retErr, sealErr) } - return retErr + return } // StepDown is used to step down from leadership @@ -1515,7 +1517,7 @@ func (c *Core) StepDown(req *logical.Request) (retErr error) { // sealInternal is an internal method used to seal the vault. It does not do // any authorization checking. The stateLock must be held prior to calling. -func (c *Core) sealInternal() error { +func (c *Core) sealInternal(keepLock bool) error { if c.sealed { return nil } @@ -1539,13 +1541,24 @@ func (c *Core) sealInternal() error { return fmt.Errorf("internal error") } } else { - // Signal the standby goroutine to shutdown, wait for completion - close(c.standbyStopCh) + // If we are trying to acquire the lock, force it to return with nil so + // runStandby will exit + if c.lockAcquisitionStopCh != nil { + c.logger.Trace("core: closing lock acquisition stop channel") + close(c.lockAcquisitionStopCh) + c.lockAcquisitionStopCh = nil + } + // If we are active, signal the standby goroutine to shut down and wait + // for completion. We have the state lock here so nothing else should + // be toggling standby status. + if !c.standby { + c.standbyStopCh <- StopOptions{KeepLock: keepLock} + c.logger.Trace("core: finished triggering standbyStopCh for runStandby") - // Release the lock while we wait to avoid deadlocking - c.stateLock.Unlock() - <-c.standbyDoneCh - c.stateLock.Lock() + // Wait for runStandby to stop + <-c.standbyDoneCh + c.logger.Trace("core: runStandby done") + } } c.logger.Debug("core: sealing barrier") @@ -1744,7 +1757,7 @@ func stopReplicationImpl(c *Core) error { // runStandby is a long running routine that is used when an HA backend // is enabled. It waits until we are leader and switches this Vault to // active. -func (c *Core) runStandby(doneCh, stopCh, manualStepDownCh chan struct{}) { +func (c *Core) runStandby(doneCh, manualStepDownCh, lockAcquisitionStopCh chan struct{}, stopCh chan StopOptions) { defer close(doneCh) defer close(manualStepDownCh) c.logger.Info("core: entering standby mode") @@ -1758,18 +1771,29 @@ func (c *Core) runStandby(doneCh, stopCh, manualStepDownCh chan struct{}) { checkLeaderStop := make(chan struct{}) go c.periodicLeaderRefresh(checkLeaderDone, checkLeaderStop) defer func() { + c.logger.Trace("core: closed periodic key rotation checker stop channel") close(keyRotateStop) <-keyRotateDone close(checkLeaderStop) + c.logger.Trace("core: closed periodic leader refresh stop channel") <-checkLeaderDone + c.logger.Trace("core: periodic leader refresh returned") }() + var manualStepDown bool for { // Check for a shutdown select { case <-stopCh: + c.logger.Trace("core: stop channel triggered in runStandby") return default: + // If we've just down, we could instantly grab the lock again. Give + // the other nodes a chance. + if manualStepDown { + time.Sleep(manualStepDownSleepPeriod) + manualStepDown = false + } } // Create a lock @@ -1785,7 +1809,7 @@ func (c *Core) runStandby(doneCh, stopCh, manualStepDownCh chan struct{}) { } // Attempt the acquisition - leaderLostCh := c.acquireLock(lock, stopCh) + leaderLostCh := c.acquireLock(lock, lockAcquisitionStopCh) // Bail if we are being shutdown if leaderLostCh == nil { @@ -1801,6 +1825,17 @@ func (c *Core) runStandby(doneCh, stopCh, manualStepDownCh chan struct{}) { // before advertising; c.stateLock.Lock() + if c.sealed { + c.logger.Warn("core: grabbed HA lock but already sealed, exiting") + lock.Unlock() + c.stateLock.Unlock() + metrics.MeasureSince([]string{"core", "leadership_setup_failed"}, activeTime) + return + } + + // Store the lock so that we can manually clear it later if needed + c.heldHALock = lock + // We haven't run postUnseal yet so we have nothing meaningful to use here ctx := context.Background() @@ -1818,10 +1853,11 @@ func (c *Core) runStandby(doneCh, stopCh, manualStepDownCh chan struct{}) { // statelock and have this shut us down; sealInternal has a // workflow where it watches for the stopCh to close so we want // to return from here - go c.Shutdown() c.logger.Error("core: error performing key upgrades", "error", err) - c.stateLock.Unlock() + go c.Shutdown() + c.heldHALock = nil lock.Unlock() + c.stateLock.Unlock() metrics.MeasureSince([]string{"core", "leadership_setup_failed"}, activeTime) return } @@ -1834,18 +1870,20 @@ func (c *Core) runStandby(doneCh, stopCh, manualStepDownCh chan struct{}) { c.localClusterPrivateKey.Store((*ecdsa.PrivateKey)(nil)) if err := c.setupCluster(ctx); err != nil { + c.heldHALock = nil + lock.Unlock() c.stateLock.Unlock() c.logger.Error("core: cluster setup failed", "error", err) - lock.Unlock() metrics.MeasureSince([]string{"core", "leadership_setup_failed"}, activeTime) continue } // Advertise as leader if err := c.advertiseLeader(ctx, uuid, leaderLostCh); err != nil { + c.heldHALock = nil + lock.Unlock() c.stateLock.Unlock() c.logger.Error("core: leader advertisement setup failed", "error", err) - lock.Unlock() metrics.MeasureSince([]string{"core", "leadership_setup_failed"}, activeTime) continue } @@ -1855,6 +1893,7 @@ func (c *Core) runStandby(doneCh, stopCh, manualStepDownCh chan struct{}) { if err == nil { c.standby = false } + c.stateLock.Unlock() // Handle a failure to unseal @@ -1866,12 +1905,16 @@ func (c *Core) runStandby(doneCh, stopCh, manualStepDownCh chan struct{}) { } // Monitor a loss of leadership - var manualStepDown bool + var stopOpts StopOptions + grabStateLock := true select { case <-leaderLostCh: c.logger.Warn("core: leadership lost, stopping active operation") - case <-stopCh: - c.logger.Warn("core: stopping active operation") + case stopOpts = <-stopCh: + // This case comes from sealInternal; we will already be having the + // state lock held so we do toggle grabStateLock to false + grabStateLock = false + close(stopCh) case <-manualStepDownCh: c.logger.Warn("core: stepping down from active operation to standby") manualStepDown = true @@ -1879,35 +1922,33 @@ func (c *Core) runStandby(doneCh, stopCh, manualStepDownCh chan struct{}) { metrics.MeasureSince([]string{"core", "leadership_lost"}, activeTime) - // Clear ourself as leader - if err := c.clearLeader(uuid); err != nil { - c.logger.Error("core: clearing leader advertisement failed", "error", err) - } - // Tell any requests that know about this to stop if c.activeContextCancelFunc != nil { c.activeContextCancelFunc() } // Attempt the pre-seal process - c.stateLock.Lock() + if grabStateLock { + c.stateLock.Lock() + } c.standby = true preSealErr := c.preSeal() - c.stateLock.Unlock() + if grabStateLock { + c.stateLock.Unlock() + } - // Give up leadership - lock.Unlock() + if !stopOpts.KeepLock { + if err := c.clearLeader(uuid); err != nil { + c.logger.Error("core: clearing leader advertisement failed", "error", err) + } + c.heldHALock.Unlock() + c.heldHALock = nil + } // Check for a failure to prepare to seal if preSealErr != nil { c.logger.Error("core: pre-seal teardown failed", "error", err) } - - // If we've merely stepped down, we could instantly grab the lock - // again. Give the other nodes a chance. - if manualStepDown { - time.Sleep(manualStepDownSleepPeriod) - } } } @@ -1920,7 +1961,11 @@ func (c *Core) periodicLeaderRefresh(doneCh, stopCh chan struct{}) { for { select { case <-time.After(leaderCheckInterval): - c.Leader() + // We do this in a goroutine because otherwise if this refresh is + // called while we're shutting down the call to Leader() can + // deadlock, which then means stopCh can never been seen and we can + // block shutdown + go c.Leader() case <-stopCh: return } diff --git a/vault/logical_system_integ_test.go b/vault/logical_system_integ_test.go index 403d4892b526..f6993b8c3813 100644 --- a/vault/logical_system_integ_test.go +++ b/vault/logical_system_integ_test.go @@ -174,11 +174,12 @@ func testPlugin_CatalogRemoved(t *testing.T, btype logical.BackendType, testMoun if sealed { t.Fatal("should not be sealed") } - // Wait for active so post-unseal takes place - // If it fails, it means unseal process failed - vault.TestWaitActive(t, core.Core) } + // Wait for active so post-unseal takes place + // If it fails, it means unseal process failed + vault.TestWaitActive(t, core.Core) + if testMount { // Mount the plugin at the same path after plugin is re-added to the catalog // and expect an error due to existing path. @@ -286,11 +287,12 @@ func testPlugin_continueOnError(t *testing.T, btype logical.BackendType, mismatc if sealed { t.Fatal("should not be sealed") } - // Wait for active so post-unseal takes place - // If it fails, it means unseal process failed - vault.TestWaitActive(t, core.Core) } + // Wait for active so post-unseal takes place + // If it fails, it means unseal process failed + vault.TestWaitActive(t, core.Core) + // Re-add the plugin to the catalog switch btype { case logical.TypeLogical: @@ -394,10 +396,11 @@ func TestSystemBackend_Plugin_SealUnseal(t *testing.T) { if sealed { t.Fatal("should not be sealed") } - // Wait for active so post-unseal takes place - // If it fails, it means unseal process failed - vault.TestWaitActive(t, core.Core) } + + // Wait for active so post-unseal takes place + // If it fails, it means unseal process failed + vault.TestWaitActive(t, cluster.Cores[0].Core) } func TestSystemBackend_Plugin_reload(t *testing.T) { From b334a3ead7011e5995cc158c808c4d685bde146b Mon Sep 17 00:00:00 2001 From: Brian Kassouf Date: Tue, 6 Mar 2018 18:35:58 -0800 Subject: [PATCH 02/45] Revert stopoptions (#4088) * Use an atomic value instead to communicate whether to give up HA lock * Remove now-unneeded StopOptions struct * Add a channel based mutex acquisition function to avoid a deadlock * Make periodic leader refresh only spawn a single waiting goroutine and extend logic to the key upgrade check --- vault/core.go | 137 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 87 insertions(+), 50 deletions(-) diff --git a/vault/core.go b/vault/core.go index 535c71e65479..3e97c0de1768 100644 --- a/vault/core.go +++ b/vault/core.go @@ -110,10 +110,6 @@ var ( LastRemoteWAL = lastRemoteWALImpl ) -type StopOptions struct { - KeepLock bool -} - // NonFatalError is an error that can be returned during NewCore that should be // displayed but not cause a program exit type NonFatalError struct { @@ -194,12 +190,12 @@ type Core struct { stateLock sync.RWMutex sealed bool - standby bool - standbyDoneCh chan struct{} - lockAcquisitionStopCh chan struct{} - standbyStopCh chan StopOptions - manualStepDownCh chan struct{} - heldHALock physical.Lock + standby bool + standbyDoneCh chan struct{} + standbyStopCh chan struct{} + manualStepDownCh chan struct{} + keepHALockOnStepDown uint32 + heldHALock physical.Lock // unlockInfo has the keys provided to Unseal until the threshold number of parts is available, as well as the operation nonce unlockInfo *unlockInformation @@ -1243,9 +1239,8 @@ func (c *Core) unsealInternal(ctx context.Context, masterKey []byte) (bool, erro // Go to standby mode, wait until we are active to unseal c.standbyDoneCh = make(chan struct{}) c.manualStepDownCh = make(chan struct{}) - c.lockAcquisitionStopCh = make(chan struct{}) - c.standbyStopCh = make(chan StopOptions) - go c.runStandby(c.standbyDoneCh, c.manualStepDownCh, c.lockAcquisitionStopCh, c.standbyStopCh) + c.standbyStopCh = make(chan struct{}) + go c.runStandby(c.standbyDoneCh, c.manualStepDownCh, c.standbyStopCh) } // Success! @@ -1541,24 +1536,21 @@ func (c *Core) sealInternal(keepLock bool) error { return fmt.Errorf("internal error") } } else { + if keepLock { + atomic.StoreUint32(&c.keepHALockOnStepDown, 1) + } // If we are trying to acquire the lock, force it to return with nil so // runStandby will exit - if c.lockAcquisitionStopCh != nil { - c.logger.Trace("core: closing lock acquisition stop channel") - close(c.lockAcquisitionStopCh) - c.lockAcquisitionStopCh = nil - } // If we are active, signal the standby goroutine to shut down and wait // for completion. We have the state lock here so nothing else should // be toggling standby status. - if !c.standby { - c.standbyStopCh <- StopOptions{KeepLock: keepLock} - c.logger.Trace("core: finished triggering standbyStopCh for runStandby") + close(c.standbyStopCh) + c.logger.Trace("core: finished triggering standbyStopCh for runStandby") - // Wait for runStandby to stop - <-c.standbyDoneCh - c.logger.Trace("core: runStandby done") - } + // Wait for runStandby to stop + <-c.standbyDoneCh + atomic.StoreUint32(&c.keepHALockOnStepDown, 0) + c.logger.Trace("core: runStandby done") } c.logger.Debug("core: sealing barrier") @@ -1757,7 +1749,7 @@ func stopReplicationImpl(c *Core) error { // runStandby is a long running routine that is used when an HA backend // is enabled. It waits until we are leader and switches this Vault to // active. -func (c *Core) runStandby(doneCh, manualStepDownCh, lockAcquisitionStopCh chan struct{}, stopCh chan StopOptions) { +func (c *Core) runStandby(doneCh, manualStepDownCh, stopCh chan struct{}) { defer close(doneCh) defer close(manualStepDownCh) c.logger.Info("core: entering standby mode") @@ -1809,7 +1801,7 @@ func (c *Core) runStandby(doneCh, manualStepDownCh, lockAcquisitionStopCh chan s } // Attempt the acquisition - leaderLostCh := c.acquireLock(lock, lockAcquisitionStopCh) + leaderLostCh := c.acquireLock(lock, stopCh) // Bail if we are being shutdown if leaderLostCh == nil { @@ -1823,7 +1815,31 @@ func (c *Core) runStandby(doneCh, manualStepDownCh, lockAcquisitionStopCh chan s // Grab the lock as we need it for cluster setup, which needs to happen // before advertising; - c.stateLock.Lock() + + lockGrabbedCh := make(chan struct{}) + go func() { + // Grab the lock + c.stateLock.Lock() + // If stopCh has been closed, which only happens while the + // stateLock is held, we have actually terminated, so we just + // instantly give up the lock, otherwise we notify that it's ready + // for consumption + select { + case <-stopCh: + c.stateLock.Unlock() + default: + close(lockGrabbedCh) + } + }() + + select { + case <-stopCh: + lock.Unlock() + metrics.MeasureSince([]string{"core", "leadership_setup_failed"}, activeTime) + return + case <-lockGrabbedCh: + // We now have the lock and can use it + } if c.sealed { c.logger.Warn("core: grabbed HA lock but already sealed, exiting") @@ -1905,16 +1921,18 @@ func (c *Core) runStandby(doneCh, manualStepDownCh, lockAcquisitionStopCh chan s } // Monitor a loss of leadership - var stopOpts StopOptions + releaseHALock := true grabStateLock := true select { case <-leaderLostCh: c.logger.Warn("core: leadership lost, stopping active operation") - case stopOpts = <-stopCh: + case <-stopCh: // This case comes from sealInternal; we will already be having the // state lock held so we do toggle grabStateLock to false + if atomic.LoadUint32(&c.keepHALockOnStepDown) == 1 { + releaseHALock = false + } grabStateLock = false - close(stopCh) case <-manualStepDownCh: c.logger.Warn("core: stepping down from active operation to standby") manualStepDown = true @@ -1937,7 +1955,7 @@ func (c *Core) runStandby(doneCh, manualStepDownCh, lockAcquisitionStopCh chan s c.stateLock.Unlock() } - if !stopOpts.KeepLock { + if releaseHALock { if err := c.clearLeader(uuid); err != nil { c.logger.Error("core: clearing leader advertisement failed", "error", err) } @@ -1958,14 +1976,23 @@ func (c *Core) runStandby(doneCh, manualStepDownCh, lockAcquisitionStopCh chan s // the result. func (c *Core) periodicLeaderRefresh(doneCh, stopCh chan struct{}) { defer close(doneCh) + var opCount int32 for { select { case <-time.After(leaderCheckInterval): + count := atomic.AddInt32(&opCount, 1) + if count > 1 { + atomic.AddInt32(&opCount, -1) + continue + } // We do this in a goroutine because otherwise if this refresh is // called while we're shutting down the call to Leader() can // deadlock, which then means stopCh can never been seen and we can // block shutdown - go c.Leader() + go func() { + defer atomic.AddInt32(&opCount, -1) + c.Leader() + }() case <-stopCh: return } @@ -1975,30 +2002,40 @@ func (c *Core) periodicLeaderRefresh(doneCh, stopCh chan struct{}) { // periodicCheckKeyUpgrade is used to watch for key rotation events as a standby func (c *Core) periodicCheckKeyUpgrade(ctx context.Context, doneCh, stopCh chan struct{}) { defer close(doneCh) + var opCount int32 for { select { case <-time.After(keyRotateCheckInterval): - // Only check if we are a standby - c.stateLock.RLock() - standby := c.standby - c.stateLock.RUnlock() - if !standby { + count := atomic.AddInt32(&opCount, 1) + if count > 1 { + atomic.AddInt32(&opCount, -1) continue } - // Check for a poison pill. If we can read it, it means we have stale - // keys (e.g. from replication being activated) and we need to seal to - // be unsealed again. - entry, _ := c.barrier.Get(ctx, poisonPillPath) - if entry != nil && len(entry.Value) > 0 { - c.logger.Warn("core: encryption keys have changed out from underneath us (possibly due to replication enabling), must be unsealed again") - go c.Shutdown() - continue - } + go func() { + defer atomic.AddInt32(&opCount, -1) + // Only check if we are a standby + c.stateLock.RLock() + standby := c.standby + c.stateLock.RUnlock() + if !standby { + return + } - if err := c.checkKeyUpgrades(ctx); err != nil { - c.logger.Error("core: key rotation periodic upgrade check failed", "error", err) - } + // Check for a poison pill. If we can read it, it means we have stale + // keys (e.g. from replication being activated) and we need to seal to + // be unsealed again. + entry, _ := c.barrier.Get(ctx, poisonPillPath) + if entry != nil && len(entry.Value) > 0 { + c.logger.Warn("core: encryption keys have changed out from underneath us (possibly due to replication enabling), must be unsealed again") + go c.Shutdown() + return + } + + if err := c.checkKeyUpgrades(ctx); err != nil { + c.logger.Error("core: key rotation periodic upgrade check failed", "error", err) + } + }() case <-stopCh: return } From 65bd8dc8b032ac2697e738e1d73567f71a1a3906 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 7 Mar 2018 09:09:37 -0500 Subject: [PATCH 03/45] Make grpc plugin client use an atomic server value to fix a data race. (#4089) Also add some coordination to ensure we don't try to clean up the grpc server before it's created/started --- logical/plugin/backend.go | 12 ++++++++++-- logical/plugin/grpc_backend_client.go | 23 +++++++++++++++++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/logical/plugin/backend.go b/logical/plugin/backend.go index b79798fb5da2..713f5d3c398a 100644 --- a/logical/plugin/backend.go +++ b/logical/plugin/backend.go @@ -3,6 +3,7 @@ package plugin import ( "context" "net/rpc" + "sync/atomic" "google.golang.org/grpc" @@ -42,10 +43,17 @@ func (b BackendPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) err } func (p *BackendPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { - return &backendGRPCPluginClient{ + ret := &backendGRPCPluginClient{ client: pb.NewBackendClient(c), clientConn: c, broker: broker, + cleanupCh: make(chan struct{}), doneCtx: ctx, - }, nil + } + + // Create the value and set the type + ret.server = new(atomic.Value) + ret.server.Store((*grpc.Server)(nil)) + + return ret, nil } diff --git a/logical/plugin/grpc_backend_client.go b/logical/plugin/grpc_backend_client.go index 7df90837d1c3..c980f795be2f 100644 --- a/logical/plugin/grpc_backend_client.go +++ b/logical/plugin/grpc_backend_client.go @@ -3,6 +3,7 @@ package plugin import ( "context" "errors" + "sync/atomic" "google.golang.org/grpc" @@ -28,8 +29,13 @@ type backendGRPCPluginClient struct { system logical.SystemView logger log.Logger + // This is used to signal to the Cleanup function that it can proceed + // because we have a defined server + cleanupCh chan struct{} + // server is the grpc server used for serving storage and sysview requests. - server *grpc.Server + server *atomic.Value + // clientConn is the underlying grpc connection to the server, we store it // so it can be cleaned up. clientConn *grpc.ClientConn @@ -139,8 +145,16 @@ func (b *backendGRPCPluginClient) Cleanup(ctx context.Context) { defer cancel() b.client.Cleanup(ctx, &pb.Empty{}) - if b.server != nil { - b.server.GracefulStop() + + // This will block until Setup has run the function to create a new server + // in b.server. If we stop here before it has a chance to actually start + // listening, when it starts listening it will immediatley error out and + // exit, which is fine. Overall this ensures that we do not miss stopping + // the server if it ends up being created after Cleanup is called. + <-b.cleanupCh + server := b.server.Load() + if server != nil { + server.(*grpc.Server).GracefulStop() } b.clientConn.Close() } @@ -184,7 +198,8 @@ func (b *backendGRPCPluginClient) Setup(ctx context.Context, config *logical.Bac s := grpc.NewServer(opts...) pb.RegisterSystemViewServer(s, sysView) pb.RegisterStorageServer(s, storage) - b.server = s + b.server.Store(s) + close(b.cleanupCh) return s } brokerID := b.broker.NextId() From ffdbcc41666fa1f745aa96252df0a8d34fc64311 Mon Sep 17 00:00:00 2001 From: Brian Nuszkowski Date: Wed, 7 Mar 2018 23:26:33 -0500 Subject: [PATCH 04/45] Return value when reading a SSH CA Role (#4098) --- builtin/logical/ssh/path_roles.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin/logical/ssh/path_roles.go b/builtin/logical/ssh/path_roles.go index 0d21d17b6ec0..44954a91e287 100644 --- a/builtin/logical/ssh/path_roles.go +++ b/builtin/logical/ssh/path_roles.go @@ -530,6 +530,7 @@ func (b *backend) parseRole(role *sshRole) (map[string]interface{}, error) { "allow_user_key_ids": role.AllowUserKeyIDs, "key_id_format": role.KeyIDFormat, "key_type": role.KeyType, + "key_bits": role.KeyBits, "default_critical_options": role.DefaultCriticalOptions, "default_extensions": role.DefaultExtensions, } From 6c04040432ec965fa0b61a2a73cfa690e24b9a6f Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 7 Mar 2018 23:27:30 -0500 Subject: [PATCH 05/45] changelog++ --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00979390e536..d39ccbdc6f13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ BUG FIXES: * cli: Improve error messages around `vault auth help` when there is no CLI helper for a particular method [GH-4056] + * secret/ssh: Return `key_bits` value when reading a role [GH-4098] ## 0.9.5 (February 26th, 2018) From 026113daa19a7ea1d0a5c143e9731de1e2daa129 Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Thu, 8 Mar 2018 10:58:43 -0500 Subject: [PATCH 06/45] Some small website fixes (#4087) * prepend first instance of 'Vault' with 'HashiCorp' * update dependencies + middleman-hashicorp --- website/Gemfile | 2 +- website/Gemfile.lock | 40 +++++++++++++++++------------------ website/source/index.html.erb | 2 +- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/website/Gemfile b/website/Gemfile index bb10a536ebbb..d21d35e45145 100644 --- a/website/Gemfile +++ b/website/Gemfile @@ -1,3 +1,3 @@ source "https://rubygems.org" -gem "middleman-hashicorp", "0.3.29" +gem "middleman-hashicorp", "0.3.30" diff --git a/website/Gemfile.lock b/website/Gemfile.lock index 843cf36e18f1..e567bf2edeb2 100644 --- a/website/Gemfile.lock +++ b/website/Gemfile.lock @@ -6,7 +6,7 @@ GEM minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - autoprefixer-rails (7.1.5) + autoprefixer-rails (8.1.0) execjs bootstrap-sass (3.3.7) autoprefixer-rails (>= 5.2.1) @@ -18,7 +18,7 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) - chunky_png (1.3.8) + chunky_png (1.3.10) coffee-script (2.4.1) coffee-script-source execjs @@ -41,7 +41,7 @@ GEM erubis (2.7.0) eventmachine (1.2.5) execjs (2.7.0) - ffi (1.9.18) + ffi (1.9.23) haml (5.0.4) temple (>= 0.8.0) tilt @@ -51,7 +51,7 @@ GEM http_parser.rb (0.6.0) i18n (0.7.0) json (2.1.0) - kramdown (1.15.0) + kramdown (1.16.2) listen (3.0.8) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -78,7 +78,7 @@ GEM rack (>= 1.4.5, < 2.0) thor (>= 0.15.2, < 2.0) tilt (~> 1.4.1, < 2.0) - middleman-hashicorp (0.3.29) + middleman-hashicorp (0.3.30) bootstrap-sass (~> 3.3) builder (~> 3.2) middleman (~> 3.4) @@ -102,22 +102,22 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) mini_portile2 (2.3.0) - minitest (5.10.3) - multi_json (1.12.2) - nokogiri (1.8.1) + minitest (5.11.3) + multi_json (1.13.1) + nokogiri (1.8.2) mini_portile2 (~> 2.3.0) - padrino-helpers (0.12.8.1) + padrino-helpers (0.12.9) i18n (~> 0.6, >= 0.6.7) - padrino-support (= 0.12.8.1) - tilt (~> 1.4.1) - padrino-support (0.12.8.1) + padrino-support (= 0.12.9) + tilt (>= 1.4.1, < 3) + padrino-support (0.12.9) activesupport (>= 3.1) - rack (1.6.8) + rack (1.6.9) rack-livereload (0.3.16) rack - rack-test (0.7.0) + rack-test (0.8.3) rack (>= 1.0, < 3) - rb-fsevent (0.10.2) + rb-fsevent (0.10.3) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) redcarpet (3.4.0) @@ -137,10 +137,10 @@ GEM thor (0.20.0) thread_safe (0.3.6) tilt (1.4.1) - turbolinks (5.0.1) - turbolinks-source (~> 5) - turbolinks-source (5.0.3) - tzinfo (1.2.3) + turbolinks (5.1.0) + turbolinks-source (~> 5.1) + turbolinks-source (5.1.0) + tzinfo (1.2.5) thread_safe (~> 0.1) uber (0.0.15) uglifier (2.7.2) @@ -153,7 +153,7 @@ PLATFORMS ruby DEPENDENCIES - middleman-hashicorp (= 0.3.29) + middleman-hashicorp (= 0.3.30) BUNDLED WITH 1.16.1 diff --git a/website/source/index.html.erb b/website/source/index.html.erb index 26b0bf091ea2..0d54cb76d4a0 100644 --- a/website/source/index.html.erb +++ b/website/source/index.html.erb @@ -23,7 +23,7 @@ description: |-

- Vault secures, stores, and tightly + HashiCorp Vault secures, stores, and tightly controls access to tokens, passwords, certificates, API keys, and other secrets in modern computing. Vault handles leasing, key revocation, key rolling, and auditing. Through a unified From a439005a30e48f80b8efe3ce12b7302fb6d4a1bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A1ndor=20Istv=C3=A1n=20Kr=C3=A1cser?= Date: Thu, 8 Mar 2018 17:01:07 +0100 Subject: [PATCH 07/45] Fix typo (remove +is) (#4104) --- api/renewer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/renewer.go b/api/renewer.go index 2a72ebe2ef66..b50cf814f376 100644 --- a/api/renewer.go +++ b/api/renewer.go @@ -162,8 +162,8 @@ func (r *Renewer) Stop() { } // Renew starts a background process for renewing this secret. When the secret -// is has auth data, this attempts to renew the auth (token). When the secret -// has a lease, this attempts to renew the lease. +// has auth data, this attempts to renew the auth (token). When the secret has +// a lease, this attempts to renew the lease. func (r *Renewer) Renew() { var result error if r.secret.Auth != nil { From 14eef27c288ff7b34f9bb73037ff9f7f82797d0d Mon Sep 17 00:00:00 2001 From: Viacheslav Vasilyev Date: Thu, 8 Mar 2018 18:01:46 +0200 Subject: [PATCH 08/45] Fix autoreplacing issue (#4103) --- .../source/guides/secret-mgmt/dynamic-secrets.html.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/website/source/guides/secret-mgmt/dynamic-secrets.html.md b/website/source/guides/secret-mgmt/dynamic-secrets.html.md index bd6057ace8cf..2337526de1c7 100644 --- a/website/source/guides/secret-mgmt/dynamic-secrets.html.md +++ b/website/source/guides/secret-mgmt/dynamic-secrets.html.md @@ -1,9 +1,9 @@ --- layout: "guides" page_title: "Secret as a Service - Guides" -sidebar_current: "guides-secret-mgmt-dataynamic-secrets" +sidebar_current: "guides-secret-mgmt-dynamic-secrets" description: |- - Vault can dynamically generate secrets on--dataemand for some systems. + Vault can dynamically generate secrets on-demand for some systems. --- # Secret as a Service: Dynamic Secrets @@ -330,7 +330,7 @@ $ vault token create -policy="apps" Key Value --- ----- token e4bdf7dc-cbbf-1bb1-c06c-6a4f9a826cf2 -token_accessor 54700b7e--data828-a6c4-6141-96e71e002bd7 +token_accessor 54700b7e-d828-a6c4-6141-96e71e002bd7 token_duration 768h0m0s token_renewable true token_policies [apps default] @@ -354,7 +354,7 @@ $ vault read database/creds/readonly Key Value --- ----- -lease_id database/creds/readonly/4b5c6e82--dataf88-0dec-c0cb-f07eee8f0329 +lease_id database/creds/readonly/4b5c6e82-df88-0dec-c0cb-f07eee8f0329 lease_duration 1h0m0s lease_renewable true password A1a-4urzp0wu92r5s1q0 @@ -496,7 +496,7 @@ user name exists. ## Next steps -This guide discussed how to generate credentials on--dataemand so that the access +This guide discussed how to generate credentials on-demand so that the access credentials no longer need to be written to disk. Next, learn about the [Tokens and Leases](/guides/identity/lease.html) so that you can control the lifecycle of those credentials. From 3024869fdca7e289048d443e313f84ffc6acb904 Mon Sep 17 00:00:00 2001 From: Jim Kalafut Date: Thu, 8 Mar 2018 08:02:04 -0800 Subject: [PATCH 09/45] Fix instruction in installation docs (#4097) --- website/source/docs/install/index.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/install/index.html.md b/website/source/docs/install/index.html.md index 36eb9307252e..af3137d29117 100644 --- a/website/source/docs/install/index.html.md +++ b/website/source/docs/install/index.html.md @@ -41,7 +41,7 @@ as a copy of [`git`](https://www.git-scm.com/) in your `PATH`. 1. Clone the Vault repository from GitHub into your `GOPATH`: ```shell - $ mkdir -p $GOPATH/src/github.com/hashicorp && cd $! + $ mkdir -p $GOPATH/src/github.com/hashicorp && cd $_ $ git clone https://github.com/hashicorp/vault.git $ cd vault ``` From 135cb4e6871a75c3b996bf8ac719767560268732 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Thu, 8 Mar 2018 08:07:51 -0800 Subject: [PATCH 10/45] Fix AWS auth max_ttl being ignored when ttl is not set (#4086) If ttl is not set, the value of `resp.Auth.TTL` is 0, resulting in the max TTL check being skipped. Also fixes the formatting of the warning message. --- builtin/credential/aws/path_login.go | 35 +++++++++++++++++++--------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/builtin/credential/aws/path_login.go b/builtin/credential/aws/path_login.go index c6d98ab86e86..b806c0c9f157 100644 --- a/builtin/credential/aws/path_login.go +++ b/builtin/credential/aws/path_login.go @@ -801,11 +801,18 @@ func (b *backend) pathLoginUpdateEc2(ctx context.Context, req *logical.Request, } if roleEntry.MaxTTL > time.Duration(0) { + shortestTTL := b.System().DefaultLeaseTTL() + if roleEntry.TTL > time.Duration(0) && roleEntry.TTL < shortestTTL { + shortestTTL = roleEntry.TTL + } + // Cap TTL to shortestMaxTTL - if resp.Auth.TTL > shortestMaxTTL { - resp.AddWarning(fmt.Sprintf("Effective TTL of '%s' exceeded the effective max_ttl of '%s'; TTL value is capped accordingly", (resp.Auth.TTL / time.Second), (shortestMaxTTL / time.Second))) - resp.Auth.TTL = shortestMaxTTL + if shortestTTL > shortestMaxTTL { + resp.AddWarning(fmt.Sprintf("Effective TTL of '%s' exceeded the effective max_ttl of '%s'; TTL value is capped accordingly", shortestTTL, shortestMaxTTL)) + shortestTTL = shortestMaxTTL } + + resp.Auth.TTL = shortestTTL } return resp, nil @@ -1309,17 +1316,23 @@ func (b *backend) pathLoginUpdateIam(ctx context.Context, req *logical.Request, } if roleEntry.MaxTTL > time.Duration(0) { - // Cap maxTTL to the sysview's max TTL - maxTTL := roleEntry.MaxTTL - if maxTTL > b.System().MaxLeaseTTL() { - maxTTL = b.System().MaxLeaseTTL() + shortestMaxTTL := b.System().MaxLeaseTTL() + if roleEntry.MaxTTL < shortestMaxTTL { + shortestMaxTTL = roleEntry.MaxTTL } - // Cap TTL to MaxTTL - if resp.Auth.TTL > maxTTL { - resp.AddWarning(fmt.Sprintf("Effective TTL of '%s' exceeded the effective max_ttl of '%s'; TTL value is capped accordingly", (resp.Auth.TTL / time.Second), (maxTTL / time.Second))) - resp.Auth.TTL = maxTTL + shortestTTL := b.System().DefaultLeaseTTL() + if roleEntry.TTL > time.Duration(0) && roleEntry.TTL < shortestTTL { + shortestTTL = roleEntry.TTL } + + // Cap TTL to shortestMaxTTL + if shortestTTL > shortestMaxTTL { + resp.AddWarning(fmt.Sprintf("Effective TTL of '%s' exceeded the effective max_ttl of '%s'; TTL value is capped accordingly", shortestTTL, shortestMaxTTL)) + shortestTTL = shortestMaxTTL + } + + resp.Auth.TTL = shortestTTL } return resp, nil From c12894310d34b039fdfed26c9a1bc81defdee52d Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 8 Mar 2018 11:08:32 -0500 Subject: [PATCH 11/45] Revert "Fix AWS auth max_ttl being ignored when ttl is not set (#4086)" (#4105) This reverts commit 135cb4e6871a75c3b996bf8ac719767560268732. --- builtin/credential/aws/path_login.go | 35 +++++++++------------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/builtin/credential/aws/path_login.go b/builtin/credential/aws/path_login.go index b806c0c9f157..c6d98ab86e86 100644 --- a/builtin/credential/aws/path_login.go +++ b/builtin/credential/aws/path_login.go @@ -801,18 +801,11 @@ func (b *backend) pathLoginUpdateEc2(ctx context.Context, req *logical.Request, } if roleEntry.MaxTTL > time.Duration(0) { - shortestTTL := b.System().DefaultLeaseTTL() - if roleEntry.TTL > time.Duration(0) && roleEntry.TTL < shortestTTL { - shortestTTL = roleEntry.TTL - } - // Cap TTL to shortestMaxTTL - if shortestTTL > shortestMaxTTL { - resp.AddWarning(fmt.Sprintf("Effective TTL of '%s' exceeded the effective max_ttl of '%s'; TTL value is capped accordingly", shortestTTL, shortestMaxTTL)) - shortestTTL = shortestMaxTTL + if resp.Auth.TTL > shortestMaxTTL { + resp.AddWarning(fmt.Sprintf("Effective TTL of '%s' exceeded the effective max_ttl of '%s'; TTL value is capped accordingly", (resp.Auth.TTL / time.Second), (shortestMaxTTL / time.Second))) + resp.Auth.TTL = shortestMaxTTL } - - resp.Auth.TTL = shortestTTL } return resp, nil @@ -1316,23 +1309,17 @@ func (b *backend) pathLoginUpdateIam(ctx context.Context, req *logical.Request, } if roleEntry.MaxTTL > time.Duration(0) { - shortestMaxTTL := b.System().MaxLeaseTTL() - if roleEntry.MaxTTL < shortestMaxTTL { - shortestMaxTTL = roleEntry.MaxTTL + // Cap maxTTL to the sysview's max TTL + maxTTL := roleEntry.MaxTTL + if maxTTL > b.System().MaxLeaseTTL() { + maxTTL = b.System().MaxLeaseTTL() } - shortestTTL := b.System().DefaultLeaseTTL() - if roleEntry.TTL > time.Duration(0) && roleEntry.TTL < shortestTTL { - shortestTTL = roleEntry.TTL + // Cap TTL to MaxTTL + if resp.Auth.TTL > maxTTL { + resp.AddWarning(fmt.Sprintf("Effective TTL of '%s' exceeded the effective max_ttl of '%s'; TTL value is capped accordingly", (resp.Auth.TTL / time.Second), (maxTTL / time.Second))) + resp.Auth.TTL = maxTTL } - - // Cap TTL to shortestMaxTTL - if shortestTTL > shortestMaxTTL { - resp.AddWarning(fmt.Sprintf("Effective TTL of '%s' exceeded the effective max_ttl of '%s'; TTL value is capped accordingly", shortestTTL, shortestMaxTTL)) - shortestTTL = shortestMaxTTL - } - - resp.Auth.TTL = shortestTTL } return resp, nil From be04e96779d275c209e1de193f592e6d55005a23 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 8 Mar 2018 13:08:00 -0500 Subject: [PATCH 12/45] Populate AWS-generated tokens with default lease TTL to fix comparisons against role max (#4107) * Populate AWS-generated tokens with default lease TTL to fix comparisons against role max * Fix printing TTLs when capping them --- builtin/credential/aws/path_login.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/builtin/credential/aws/path_login.go b/builtin/credential/aws/path_login.go index c6d98ab86e86..4a58f23cb2de 100644 --- a/builtin/credential/aws/path_login.go +++ b/builtin/credential/aws/path_login.go @@ -800,12 +800,14 @@ func (b *backend) pathLoginUpdateEc2(ctx context.Context, req *logical.Request, resp.Auth.Metadata["nonce"] = clientNonce } - if roleEntry.MaxTTL > time.Duration(0) { - // Cap TTL to shortestMaxTTL - if resp.Auth.TTL > shortestMaxTTL { - resp.AddWarning(fmt.Sprintf("Effective TTL of '%s' exceeded the effective max_ttl of '%s'; TTL value is capped accordingly", (resp.Auth.TTL / time.Second), (shortestMaxTTL / time.Second))) - resp.Auth.TTL = shortestMaxTTL - } + // In this case no role value was set so pull in what will be assigned by + // Core for comparison + if resp.Auth.TTL == 0 { + resp.Auth.TTL = b.System().DefaultLeaseTTL() + } + if resp.Auth.TTL > shortestMaxTTL { + resp.Auth.TTL = shortestMaxTTL + resp.AddWarning(fmt.Sprintf("Effective TTL of '%s' exceeded the effective max_ttl of '%s'; TTL value is capped accordingly", resp.Auth.TTL, shortestMaxTTL)) } return resp, nil @@ -1308,6 +1310,9 @@ func (b *backend) pathLoginUpdateIam(ctx context.Context, req *logical.Request, }, } + if resp.Auth.TTL == 0 { + resp.Auth.TTL = b.System().DefaultLeaseTTL() + } if roleEntry.MaxTTL > time.Duration(0) { // Cap maxTTL to the sysview's max TTL maxTTL := roleEntry.MaxTTL @@ -1317,7 +1322,7 @@ func (b *backend) pathLoginUpdateIam(ctx context.Context, req *logical.Request, // Cap TTL to MaxTTL if resp.Auth.TTL > maxTTL { - resp.AddWarning(fmt.Sprintf("Effective TTL of '%s' exceeded the effective max_ttl of '%s'; TTL value is capped accordingly", (resp.Auth.TTL / time.Second), (maxTTL / time.Second))) + resp.AddWarning(fmt.Sprintf("Effective TTL of '%s' exceeded the effective max_ttl of '%s'; TTL value is capped accordingly", resp.Auth.TTL, maxTTL)) resp.Auth.TTL = maxTTL } } From 1aeda8d1163cdcd9b7d746f05ee9e92a637da170 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 8 Mar 2018 13:09:07 -0500 Subject: [PATCH 13/45] changelog++ --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d39ccbdc6f13..d15ab499a9ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ IMPROVEMENTS: BUG FIXES: + * auth/aws: Fix honoring `max_ttl` when a corresponding role `ttl` is not also + set [GH-4107] * cli: Improve error messages around `vault auth help` when there is no CLI helper for a particular method [GH-4056] * secret/ssh: Return `key_bits` value when reading a role [GH-4098] From c0815bd2b05b4d8238064416701852061667ab94 Mon Sep 17 00:00:00 2001 From: Brian Kassouf Date: Thu, 8 Mar 2018 11:21:11 -0800 Subject: [PATCH 14/45] Add context to the NewSalt function (#4102) --- audit/audit.go | 2 +- audit/format.go | 11 +++--- audit/format_json.go | 7 ++-- audit/format_json_test.go | 7 ++-- audit/format_jsonx.go | 7 ++-- audit/format_jsonx_test.go | 7 ++-- audit/format_test.go | 13 ++++--- audit/formatter.go | 5 ++- audit/hashstructure_test.go | 4 +- builtin/audit/file/backend.go | 28 +++++++------- builtin/audit/socket/backend.go | 12 +++--- builtin/audit/syslog/backend.go | 16 ++++---- builtin/credential/app-id/backend.go | 4 +- builtin/credential/app-id/backend_test.go | 2 +- builtin/credential/approle/backend.go | 4 +- builtin/credential/approle/path_role.go | 6 +-- .../credential/approle/path_tidy_user_id.go | 2 +- builtin/credential/approle/validation.go | 6 +-- builtin/logical/ssh/backend.go | 4 +- builtin/logical/ssh/path_creds_create.go | 10 ++--- builtin/logical/ssh/path_verify.go | 2 +- builtin/logical/ssh/secret_dynamic_key.go | 2 +- builtin/logical/ssh/secret_otp.go | 2 +- builtin/logical/ssh/util.go | 4 +- helper/salt/salt.go | 6 +-- helper/salt/salt_test.go | 4 +- logical/framework/path_map.go | 4 +- logical/framework/path_map_test.go | 6 +-- vault/audit_broker.go | 8 ++-- vault/audit_test.go | 8 ++-- vault/audited_headers.go | 4 +- vault/audited_headers_test.go | 10 ++--- vault/expiration.go | 24 ++++++------ vault/logical_system.go | 2 +- vault/router.go | 4 +- vault/testing.go | 8 ++-- vault/token_store.go | 38 +++++++++---------- vault/token_store_test.go | 14 +++---- 38 files changed, 157 insertions(+), 150 deletions(-) diff --git a/audit/audit.go b/audit/audit.go index 6adf3b8bcb26..fed7033500ab 100644 --- a/audit/audit.go +++ b/audit/audit.go @@ -27,7 +27,7 @@ type Backend interface { // GetHash is used to return the given data with the backend's hash, // so that a caller can determine if a value in the audit log matches // an expected plaintext value - GetHash(string) (string, error) + GetHash(context.Context, string) (string, error) // Reload is called on SIGHUP for supporting backends. Reload(context.Context) error diff --git a/audit/format.go b/audit/format.go index aaa92a5730a9..f226c95856c8 100644 --- a/audit/format.go +++ b/audit/format.go @@ -1,6 +1,7 @@ package audit import ( + "context" "fmt" "io" "strings" @@ -16,7 +17,7 @@ import ( type AuditFormatWriter interface { WriteRequest(io.Writer, *AuditRequestEntry) error WriteResponse(io.Writer, *AuditResponseEntry) error - Salt() (*salt.Salt, error) + Salt(context.Context) (*salt.Salt, error) } // AuditFormatter implements the Formatter interface, and allows the underlying @@ -27,7 +28,7 @@ type AuditFormatter struct { var _ Formatter = (*AuditFormatter)(nil) -func (f *AuditFormatter) FormatRequest(w io.Writer, config FormatterConfig, in *LogInput) error { +func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config FormatterConfig, in *LogInput) error { if in == nil || in.Request == nil { return fmt.Errorf("request to request-audit a nil request") } @@ -40,7 +41,7 @@ func (f *AuditFormatter) FormatRequest(w io.Writer, config FormatterConfig, in * return fmt.Errorf("no format writer specified") } - salt, err := f.Salt() + salt, err := f.Salt(ctx) if err != nil { return errwrap.Wrapf("error fetching salt: {{err}}", err) } @@ -151,7 +152,7 @@ func (f *AuditFormatter) FormatRequest(w io.Writer, config FormatterConfig, in * return f.AuditFormatWriter.WriteRequest(w, reqEntry) } -func (f *AuditFormatter) FormatResponse(w io.Writer, config FormatterConfig, in *LogInput) error { +func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config FormatterConfig, in *LogInput) error { if in == nil || in.Request == nil { return fmt.Errorf("request to response-audit a nil request") } @@ -164,7 +165,7 @@ func (f *AuditFormatter) FormatResponse(w io.Writer, config FormatterConfig, in return fmt.Errorf("no format writer specified") } - salt, err := f.Salt() + salt, err := f.Salt(ctx) if err != nil { return errwrap.Wrapf("error fetching salt: {{err}}", err) } diff --git a/audit/format_json.go b/audit/format_json.go index 0a5c9d90bdfa..f42ab20d387f 100644 --- a/audit/format_json.go +++ b/audit/format_json.go @@ -1,6 +1,7 @@ package audit import ( + "context" "encoding/json" "fmt" "io" @@ -12,7 +13,7 @@ import ( // a JSON format. type JSONFormatWriter struct { Prefix string - SaltFunc func() (*salt.Salt, error) + SaltFunc func(context.Context) (*salt.Salt, error) } func (f *JSONFormatWriter) WriteRequest(w io.Writer, req *AuditRequestEntry) error { @@ -47,6 +48,6 @@ func (f *JSONFormatWriter) WriteResponse(w io.Writer, resp *AuditResponseEntry) return enc.Encode(resp) } -func (f *JSONFormatWriter) Salt() (*salt.Salt, error) { - return f.SaltFunc() +func (f *JSONFormatWriter) Salt(ctx context.Context) (*salt.Salt, error) { + return f.SaltFunc(ctx) } diff --git a/audit/format_json_test.go b/audit/format_json_test.go index 90c44a09fa12..9ab20ac35e09 100644 --- a/audit/format_json_test.go +++ b/audit/format_json_test.go @@ -2,6 +2,7 @@ package audit import ( "bytes" + "context" "encoding/json" "strings" "testing" @@ -17,11 +18,11 @@ import ( ) func TestFormatJSON_formatRequest(t *testing.T) { - salter, err := salt.NewSalt(nil, nil) + salter, err := salt.NewSalt(context.Background(), nil, nil) if err != nil { t.Fatal(err) } - saltFunc := func() (*salt.Salt, error) { + saltFunc := func(context.Context) (*salt.Salt, error) { return salter, nil } @@ -90,7 +91,7 @@ func TestFormatJSON_formatRequest(t *testing.T) { Request: tc.Req, OuterErr: tc.Err, } - if err := formatter.FormatRequest(&buf, config, in); err != nil { + if err := formatter.FormatRequest(context.Background(), &buf, config, in); err != nil { t.Fatalf("bad: %s\nerr: %s", name, err) } diff --git a/audit/format_jsonx.go b/audit/format_jsonx.go index 792e5524c384..30937464df5e 100644 --- a/audit/format_jsonx.go +++ b/audit/format_jsonx.go @@ -1,6 +1,7 @@ package audit import ( + "context" "encoding/json" "fmt" "io" @@ -13,7 +14,7 @@ import ( // a XML format. type JSONxFormatWriter struct { Prefix string - SaltFunc func() (*salt.Salt, error) + SaltFunc func(context.Context) (*salt.Salt, error) } func (f *JSONxFormatWriter) WriteRequest(w io.Writer, req *AuditRequestEntry) error { @@ -68,6 +69,6 @@ func (f *JSONxFormatWriter) WriteResponse(w io.Writer, resp *AuditResponseEntry) return err } -func (f *JSONxFormatWriter) Salt() (*salt.Salt, error) { - return f.SaltFunc() +func (f *JSONxFormatWriter) Salt(ctx context.Context) (*salt.Salt, error) { + return f.SaltFunc(ctx) } diff --git a/audit/format_jsonx_test.go b/audit/format_jsonx_test.go index c9096430b7a4..8775ef570b1d 100644 --- a/audit/format_jsonx_test.go +++ b/audit/format_jsonx_test.go @@ -2,6 +2,7 @@ package audit import ( "bytes" + "context" "strings" "testing" "time" @@ -15,11 +16,11 @@ import ( ) func TestFormatJSONx_formatRequest(t *testing.T) { - salter, err := salt.NewSalt(nil, nil) + salter, err := salt.NewSalt(context.Background(), nil, nil) if err != nil { t.Fatal(err) } - saltFunc := func() (*salt.Salt, error) { + saltFunc := func(context.Context) (*salt.Salt, error) { return salter, nil } @@ -94,7 +95,7 @@ func TestFormatJSONx_formatRequest(t *testing.T) { Request: tc.Req, OuterErr: tc.Err, } - if err := formatter.FormatRequest(&buf, config, in); err != nil { + if err := formatter.FormatRequest(context.Background(), &buf, config, in); err != nil { t.Fatalf("bad: %s\nerr: %s", name, err) } diff --git a/audit/format_test.go b/audit/format_test.go index 54da56154f66..e0bd68f0b341 100644 --- a/audit/format_test.go +++ b/audit/format_test.go @@ -1,6 +1,7 @@ package audit import ( + "context" "io" "io/ioutil" "testing" @@ -22,12 +23,12 @@ func (n *noopFormatWriter) WriteResponse(_ io.Writer, _ *AuditResponseEntry) err return nil } -func (n *noopFormatWriter) Salt() (*salt.Salt, error) { +func (n *noopFormatWriter) Salt(ctx context.Context) (*salt.Salt, error) { if n.salt != nil { return n.salt, nil } var err error - n.salt, err = salt.NewSalt(nil, nil) + n.salt, err = salt.NewSalt(ctx, nil, nil) if err != nil { return nil, err } @@ -40,14 +41,14 @@ func TestFormatRequestErrors(t *testing.T) { AuditFormatWriter: &noopFormatWriter{}, } - if err := formatter.FormatRequest(ioutil.Discard, config, &LogInput{}); err == nil { + if err := formatter.FormatRequest(context.Background(), ioutil.Discard, config, &LogInput{}); err == nil { t.Fatal("expected error due to nil request") } in := &LogInput{ Request: &logical.Request{}, } - if err := formatter.FormatRequest(nil, config, in); err == nil { + if err := formatter.FormatRequest(context.Background(), nil, config, in); err == nil { t.Fatal("expected error due to nil writer") } } @@ -58,14 +59,14 @@ func TestFormatResponseErrors(t *testing.T) { AuditFormatWriter: &noopFormatWriter{}, } - if err := formatter.FormatResponse(ioutil.Discard, config, &LogInput{}); err == nil { + if err := formatter.FormatResponse(context.Background(), ioutil.Discard, config, &LogInput{}); err == nil { t.Fatal("expected error due to nil request") } in := &LogInput{ Request: &logical.Request{}, } - if err := formatter.FormatResponse(nil, config, in); err == nil { + if err := formatter.FormatResponse(context.Background(), nil, config, in); err == nil { t.Fatal("expected error due to nil writer") } } diff --git a/audit/formatter.go b/audit/formatter.go index d296e55b778d..7702a1ee5d64 100644 --- a/audit/formatter.go +++ b/audit/formatter.go @@ -1,6 +1,7 @@ package audit import ( + "context" "io" ) @@ -10,8 +11,8 @@ import ( // // It is recommended that you pass data through Hash prior to formatting it. type Formatter interface { - FormatRequest(io.Writer, FormatterConfig, *LogInput) error - FormatResponse(io.Writer, FormatterConfig, *LogInput) error + FormatRequest(context.Context, io.Writer, FormatterConfig, *LogInput) error + FormatResponse(context.Context, io.Writer, FormatterConfig, *LogInput) error } type FormatterConfig struct { diff --git a/audit/hashstructure_test.go b/audit/hashstructure_test.go index 21cf05b1e901..f42fa5040235 100644 --- a/audit/hashstructure_test.go +++ b/audit/hashstructure_test.go @@ -99,7 +99,7 @@ func TestHashString(t *testing.T) { Key: "salt", Value: []byte("foo"), }) - localSalt, err := salt.NewSalt(inmemStorage, &salt.Config{ + localSalt, err := salt.NewSalt(context.Background(), inmemStorage, &salt.Config{ HMAC: sha256.New, HMACType: "hmac-sha256", }) @@ -206,7 +206,7 @@ func TestHash(t *testing.T) { Key: "salt", Value: []byte("foo"), }) - localSalt, err := salt.NewSalt(inmemStorage, &salt.Config{ + localSalt, err := salt.NewSalt(context.Background(), inmemStorage, &salt.Config{ HMAC: sha256.New, HMACType: "hmac-sha256", }) diff --git a/builtin/audit/file/backend.go b/builtin/audit/file/backend.go index 7bf066d1c034..bd69c3e2bb79 100644 --- a/builtin/audit/file/backend.go +++ b/builtin/audit/file/backend.go @@ -143,7 +143,7 @@ type Backend struct { var _ audit.Backend = (*Backend)(nil) -func (b *Backend) Salt() (*salt.Salt, error) { +func (b *Backend) Salt(ctx context.Context) (*salt.Salt, error) { b.saltMutex.RLock() if b.salt != nil { defer b.saltMutex.RUnlock() @@ -155,7 +155,7 @@ func (b *Backend) Salt() (*salt.Salt, error) { if b.salt != nil { return b.salt, nil } - salt, err := salt.NewSalt(b.saltView, b.saltConfig) + salt, err := salt.NewSalt(ctx, b.saltView, b.saltConfig) if err != nil { return nil, err } @@ -163,30 +163,30 @@ func (b *Backend) Salt() (*salt.Salt, error) { return salt, nil } -func (b *Backend) GetHash(data string) (string, error) { - salt, err := b.Salt() +func (b *Backend) GetHash(ctx context.Context, data string) (string, error) { + salt, err := b.Salt(ctx) if err != nil { return "", err } return audit.HashString(salt, data), nil } -func (b *Backend) LogRequest(_ context.Context, in *audit.LogInput) error { +func (b *Backend) LogRequest(ctx context.Context, in *audit.LogInput) error { b.fileLock.Lock() defer b.fileLock.Unlock() switch b.path { case "stdout": - return b.formatter.FormatRequest(os.Stdout, b.formatConfig, in) + return b.formatter.FormatRequest(ctx, os.Stdout, b.formatConfig, in) case "discard": - return b.formatter.FormatRequest(ioutil.Discard, b.formatConfig, in) + return b.formatter.FormatRequest(ctx, ioutil.Discard, b.formatConfig, in) } if err := b.open(); err != nil { return err } - if err := b.formatter.FormatRequest(b.f, b.formatConfig, in); err == nil { + if err := b.formatter.FormatRequest(ctx, b.f, b.formatConfig, in); err == nil { return nil } @@ -198,26 +198,26 @@ func (b *Backend) LogRequest(_ context.Context, in *audit.LogInput) error { return err } - return b.formatter.FormatRequest(b.f, b.formatConfig, in) + return b.formatter.FormatRequest(ctx, b.f, b.formatConfig, in) } -func (b *Backend) LogResponse(_ context.Context, in *audit.LogInput) error { +func (b *Backend) LogResponse(ctx context.Context, in *audit.LogInput) error { b.fileLock.Lock() defer b.fileLock.Unlock() switch b.path { case "stdout": - return b.formatter.FormatResponse(os.Stdout, b.formatConfig, in) + return b.formatter.FormatResponse(ctx, os.Stdout, b.formatConfig, in) case "discard": - return b.formatter.FormatResponse(ioutil.Discard, b.formatConfig, in) + return b.formatter.FormatResponse(ctx, ioutil.Discard, b.formatConfig, in) } if err := b.open(); err != nil { return err } - if err := b.formatter.FormatResponse(b.f, b.formatConfig, in); err == nil { + if err := b.formatter.FormatResponse(ctx, b.f, b.formatConfig, in); err == nil { return nil } @@ -229,7 +229,7 @@ func (b *Backend) LogResponse(_ context.Context, in *audit.LogInput) error { return err } - return b.formatter.FormatResponse(b.f, b.formatConfig, in) + return b.formatter.FormatResponse(ctx, b.f, b.formatConfig, in) } // The file lock must be held before calling this diff --git a/builtin/audit/socket/backend.go b/builtin/audit/socket/backend.go index d99d28f574fa..e0d5b2271b18 100644 --- a/builtin/audit/socket/backend.go +++ b/builtin/audit/socket/backend.go @@ -123,8 +123,8 @@ type Backend struct { var _ audit.Backend = (*Backend)(nil) -func (b *Backend) GetHash(data string) (string, error) { - salt, err := b.Salt() +func (b *Backend) GetHash(ctx context.Context, data string) (string, error) { + salt, err := b.Salt(ctx) if err != nil { return "", err } @@ -133,7 +133,7 @@ func (b *Backend) GetHash(data string) (string, error) { func (b *Backend) LogRequest(ctx context.Context, in *audit.LogInput) error { var buf bytes.Buffer - if err := b.formatter.FormatRequest(&buf, b.formatConfig, in); err != nil { + if err := b.formatter.FormatRequest(ctx, &buf, b.formatConfig, in); err != nil { return err } @@ -156,7 +156,7 @@ func (b *Backend) LogRequest(ctx context.Context, in *audit.LogInput) error { func (b *Backend) LogResponse(ctx context.Context, in *audit.LogInput) error { var buf bytes.Buffer - if err := b.formatter.FormatResponse(&buf, b.formatConfig, in); err != nil { + if err := b.formatter.FormatResponse(ctx, &buf, b.formatConfig, in); err != nil { return err } @@ -223,7 +223,7 @@ func (b *Backend) Reload(ctx context.Context) error { return err } -func (b *Backend) Salt() (*salt.Salt, error) { +func (b *Backend) Salt(ctx context.Context) (*salt.Salt, error) { b.saltMutex.RLock() if b.salt != nil { defer b.saltMutex.RUnlock() @@ -235,7 +235,7 @@ func (b *Backend) Salt() (*salt.Salt, error) { if b.salt != nil { return b.salt, nil } - salt, err := salt.NewSalt(b.saltView, b.saltConfig) + salt, err := salt.NewSalt(ctx, b.saltView, b.saltConfig) if err != nil { return nil, err } diff --git a/builtin/audit/syslog/backend.go b/builtin/audit/syslog/backend.go index 2df25f229822..68d8a361ab1e 100644 --- a/builtin/audit/syslog/backend.go +++ b/builtin/audit/syslog/backend.go @@ -110,17 +110,17 @@ type Backend struct { var _ audit.Backend = (*Backend)(nil) -func (b *Backend) GetHash(data string) (string, error) { - salt, err := b.Salt() +func (b *Backend) GetHash(ctx context.Context, data string) (string, error) { + salt, err := b.Salt(ctx) if err != nil { return "", err } return audit.HashString(salt, data), nil } -func (b *Backend) LogRequest(_ context.Context, in *audit.LogInput) error { +func (b *Backend) LogRequest(ctx context.Context, in *audit.LogInput) error { var buf bytes.Buffer - if err := b.formatter.FormatRequest(&buf, b.formatConfig, in); err != nil { + if err := b.formatter.FormatRequest(ctx, &buf, b.formatConfig, in); err != nil { return err } @@ -129,9 +129,9 @@ func (b *Backend) LogRequest(_ context.Context, in *audit.LogInput) error { return err } -func (b *Backend) LogResponse(_ context.Context, in *audit.LogInput) error { +func (b *Backend) LogResponse(ctx context.Context, in *audit.LogInput) error { var buf bytes.Buffer - if err := b.formatter.FormatResponse(&buf, b.formatConfig, in); err != nil { + if err := b.formatter.FormatResponse(ctx, &buf, b.formatConfig, in); err != nil { return err } @@ -144,7 +144,7 @@ func (b *Backend) Reload(_ context.Context) error { return nil } -func (b *Backend) Salt() (*salt.Salt, error) { +func (b *Backend) Salt(ctx context.Context) (*salt.Salt, error) { b.saltMutex.RLock() if b.salt != nil { defer b.saltMutex.RUnlock() @@ -156,7 +156,7 @@ func (b *Backend) Salt() (*salt.Salt, error) { if b.salt != nil { return b.salt, nil } - salt, err := salt.NewSalt(b.saltView, b.saltConfig) + salt, err := salt.NewSalt(ctx, b.saltView, b.saltConfig) if err != nil { return nil, err } diff --git a/builtin/credential/app-id/backend.go b/builtin/credential/app-id/backend.go index 2cea93edabc9..32ec15875447 100644 --- a/builtin/credential/app-id/backend.go +++ b/builtin/credential/app-id/backend.go @@ -93,7 +93,7 @@ type backend struct { MapUserId *framework.PathMap } -func (b *backend) Salt() (*salt.Salt, error) { +func (b *backend) Salt(ctx context.Context) (*salt.Salt, error) { b.SaltMutex.RLock() if b.salt != nil { defer b.SaltMutex.RUnlock() @@ -105,7 +105,7 @@ func (b *backend) Salt() (*salt.Salt, error) { if b.salt != nil { return b.salt, nil } - salt, err := salt.NewSalt(b.view, &salt.Config{ + salt, err := salt.NewSalt(ctx, b.view, &salt.Config{ HashFunc: salt.SHA1Hash, Location: salt.DefaultLocation, }) diff --git a/builtin/credential/app-id/backend_test.go b/builtin/credential/app-id/backend_test.go index bff8bc77b484..e25fa9cbb7aa 100644 --- a/builtin/credential/app-id/backend_test.go +++ b/builtin/credential/app-id/backend_test.go @@ -54,7 +54,7 @@ func TestBackend_basic(t *testing.T) { if len(keys) != 1 { t.Fatalf("expected 1 key, got %d", len(keys)) } - bSalt, err := b.Salt() + bSalt, err := b.Salt(context.Background()) if err != nil { t.Fatal(err) } diff --git a/builtin/credential/approle/backend.go b/builtin/credential/approle/backend.go index 11c62648b60d..6b914685ef55 100644 --- a/builtin/credential/approle/backend.go +++ b/builtin/credential/approle/backend.go @@ -104,7 +104,7 @@ func Backend(conf *logical.BackendConfig) (*backend, error) { return b, nil } -func (b *backend) Salt() (*salt.Salt, error) { +func (b *backend) Salt(ctx context.Context) (*salt.Salt, error) { b.saltMutex.RLock() if b.salt != nil { defer b.saltMutex.RUnlock() @@ -116,7 +116,7 @@ func (b *backend) Salt() (*salt.Salt, error) { if b.salt != nil { return b.salt, nil } - salt, err := salt.NewSalt(b.view, &salt.Config{ + salt, err := salt.NewSalt(ctx, b.view, &salt.Config{ HashFunc: salt.SHA256Hash, Location: salt.DefaultLocation, }) diff --git a/builtin/credential/approle/path_role.go b/builtin/credential/approle/path_role.go index 0d3417cf4185..e1d6bfc59d96 100644 --- a/builtin/credential/approle/path_role.go +++ b/builtin/credential/approle/path_role.go @@ -2052,7 +2052,7 @@ func (b *backend) setRoleIDEntry(ctx context.Context, s logical.Storage, roleID lock.Lock() defer lock.Unlock() - salt, err := b.Salt() + salt, err := b.Salt(ctx) if err != nil { return err } @@ -2080,7 +2080,7 @@ func (b *backend) roleIDEntry(ctx context.Context, s logical.Storage, roleID str var result roleIDStorageEntry - salt, err := b.Salt() + salt, err := b.Salt(ctx) if err != nil { return nil, err } @@ -2108,7 +2108,7 @@ func (b *backend) roleIDEntryDelete(ctx context.Context, s logical.Storage, role lock.Lock() defer lock.Unlock() - salt, err := b.Salt() + salt, err := b.Salt(ctx) if err != nil { return err } diff --git a/builtin/credential/approle/path_tidy_user_id.go b/builtin/credential/approle/path_tidy_user_id.go index 437d5c8137e3..23a380153c46 100644 --- a/builtin/credential/approle/path_tidy_user_id.go +++ b/builtin/credential/approle/path_tidy_user_id.go @@ -104,7 +104,7 @@ func (b *backend) tidySecretID(ctx context.Context, s logical.Storage) error { // the corresponding accessor from the accessorMap. This will leave // only the dangling accessors in the map which can then be cleaned // up later. - salt, err := b.Salt() + salt, err := b.Salt(ctx) if err != nil { lock.Unlock() return err diff --git a/builtin/credential/approle/validation.go b/builtin/credential/approle/validation.go index 2052e70e5e85..9a1fdea95a90 100644 --- a/builtin/credential/approle/validation.go +++ b/builtin/credential/approle/validation.go @@ -480,7 +480,7 @@ func (b *backend) secretIDAccessorEntry(ctx context.Context, s logical.Storage, var result secretIDAccessorStorageEntry // Create index entry, mapping the accessor to the token ID - salt, err := b.Salt() + salt, err := b.Salt(ctx) if err != nil { return nil, err } @@ -513,7 +513,7 @@ func (b *backend) createSecretIDAccessorEntry(ctx context.Context, s logical.Sto entry.SecretIDAccessor = accessorUUID // Create index entry, mapping the accessor to the token ID - salt, err := b.Salt() + salt, err := b.Salt(ctx) if err != nil { return err } @@ -536,7 +536,7 @@ func (b *backend) createSecretIDAccessorEntry(ctx context.Context, s logical.Sto // deleteSecretIDAccessorEntry deletes the storage index mapping the accessor to a SecretID. func (b *backend) deleteSecretIDAccessorEntry(ctx context.Context, s logical.Storage, secretIDAccessor string) error { - salt, err := b.Salt() + salt, err := b.Salt(ctx) if err != nil { return err } diff --git a/builtin/logical/ssh/backend.go b/builtin/logical/ssh/backend.go index 9f12d58ad3c8..0616b8f783da 100644 --- a/builtin/logical/ssh/backend.go +++ b/builtin/logical/ssh/backend.go @@ -75,7 +75,7 @@ func Backend(conf *logical.BackendConfig) (*backend, error) { return &b, nil } -func (b *backend) Salt() (*salt.Salt, error) { +func (b *backend) Salt(ctx context.Context) (*salt.Salt, error) { b.saltMutex.RLock() if b.salt != nil { defer b.saltMutex.RUnlock() @@ -87,7 +87,7 @@ func (b *backend) Salt() (*salt.Salt, error) { if b.salt != nil { return b.salt, nil } - salt, err := salt.NewSalt(b.view, &salt.Config{ + salt, err := salt.NewSalt(ctx, b.view, &salt.Config{ HashFunc: salt.SHA256Hash, Location: salt.DefaultLocation, }) diff --git a/builtin/logical/ssh/path_creds_create.go b/builtin/logical/ssh/path_creds_create.go index 8761e6f2bf21..236cb8197f48 100644 --- a/builtin/logical/ssh/path_creds_create.go +++ b/builtin/logical/ssh/path_creds_create.go @@ -194,7 +194,7 @@ func (b *backend) GenerateDynamicCredential(ctx context.Context, req *logical.Re } // Add the public key to authorized_keys file in target machine - err = b.installPublicKeyInTarget(role.AdminUser, username, ip, role.Port, hostKey.Key, dynamicPublicKey, role.InstallScript, true) + err = b.installPublicKeyInTarget(ctx, role.AdminUser, username, ip, role.Port, hostKey.Key, dynamicPublicKey, role.InstallScript, true) if err != nil { return "", "", fmt.Errorf("failed to add public key to authorized_keys file in target: %v", err) } @@ -202,12 +202,12 @@ func (b *backend) GenerateDynamicCredential(ctx context.Context, req *logical.Re } // Generates a UUID OTP and its salted value based on the salt of the backend. -func (b *backend) GenerateSaltedOTP() (string, string, error) { +func (b *backend) GenerateSaltedOTP(ctx context.Context) (string, string, error) { str, err := uuid.GenerateUUID() if err != nil { return "", "", err } - salt, err := b.Salt() + salt, err := b.Salt(ctx) if err != nil { return "", "", err } @@ -217,7 +217,7 @@ func (b *backend) GenerateSaltedOTP() (string, string, error) { // Generates an UUID OTP and creates an entry for the same in storage backend with its salted string. func (b *backend) GenerateOTPCredential(ctx context.Context, req *logical.Request, sshOTPEntry *sshOTP) (string, error) { - otp, otpSalted, err := b.GenerateSaltedOTP() + otp, otpSalted, err := b.GenerateSaltedOTP(ctx) if err != nil { return "", err } @@ -230,7 +230,7 @@ func (b *backend) GenerateOTPCredential(ctx context.Context, req *logical.Reques // OTP is generated. It is very unlikely that this is the case and this // code is just for safety. for err == nil && entry != nil { - otp, otpSalted, err = b.GenerateSaltedOTP() + otp, otpSalted, err = b.GenerateSaltedOTP(ctx) if err != nil { return "", err } diff --git a/builtin/logical/ssh/path_verify.go b/builtin/logical/ssh/path_verify.go index 94477a662f5c..d15d2ec6cbbd 100644 --- a/builtin/logical/ssh/path_verify.go +++ b/builtin/logical/ssh/path_verify.go @@ -59,7 +59,7 @@ func (b *backend) pathVerifyWrite(ctx context.Context, req *logical.Request, d * // Create the salt of OTP because entry would have been create with the // salt and not directly of the OTP. Salt will yield the same value which // because the seed is the same, the backend salt. - salt, err := b.Salt() + salt, err := b.Salt(ctx) if err != nil { return nil, err } diff --git a/builtin/logical/ssh/secret_dynamic_key.go b/builtin/logical/ssh/secret_dynamic_key.go index 2a7083e4aa4f..fd85ac0950c2 100644 --- a/builtin/logical/ssh/secret_dynamic_key.go +++ b/builtin/logical/ssh/secret_dynamic_key.go @@ -64,7 +64,7 @@ func (b *backend) secretDynamicKeyRevoke(ctx context.Context, req *logical.Reque // Remove the public key from authorized_keys file in target machine // The last param 'false' indicates that the key should be uninstalled. - err = b.installPublicKeyInTarget(intSec.AdminUser, intSec.Username, intSec.IP, intSec.Port, hostKey.Key, intSec.DynamicPublicKey, intSec.InstallScript, false) + err = b.installPublicKeyInTarget(ctx, intSec.AdminUser, intSec.Username, intSec.IP, intSec.Port, hostKey.Key, intSec.DynamicPublicKey, intSec.InstallScript, false) if err != nil { return nil, fmt.Errorf("error removing public key from authorized_keys file in target") } diff --git a/builtin/logical/ssh/secret_otp.go b/builtin/logical/ssh/secret_otp.go index c0a71ef3194c..40f75a308105 100644 --- a/builtin/logical/ssh/secret_otp.go +++ b/builtin/logical/ssh/secret_otp.go @@ -34,7 +34,7 @@ func (b *backend) secretOTPRevoke(ctx context.Context, req *logical.Request, d * return nil, fmt.Errorf("secret is missing internal data") } - salt, err := b.Salt() + salt, err := b.Salt(ctx) if err != nil { return nil, err } diff --git a/builtin/logical/ssh/util.go b/builtin/logical/ssh/util.go index 78a9621c5206..62eaf19e4961 100644 --- a/builtin/logical/ssh/util.go +++ b/builtin/logical/ssh/util.go @@ -46,10 +46,10 @@ func generateRSAKeys(keyBits int) (publicKeyRsa string, privateKeyRsa string, er // authorized_keys file is hard coded to resemble Linux. // // The last param 'install' if false, uninstalls the key. -func (b *backend) installPublicKeyInTarget(adminUser, username, ip string, port int, hostkey, dynamicPublicKey, installScript string, install bool) error { +func (b *backend) installPublicKeyInTarget(ctx context.Context, adminUser, username, ip string, port int, hostkey, dynamicPublicKey, installScript string, install bool) error { // Transfer the newly generated public key to remote host under a random // file name. This is to avoid name collisions from other requests. - _, publicKeyFileName, err := b.GenerateSaltedOTP() + _, publicKeyFileName, err := b.GenerateSaltedOTP(ctx) if err != nil { return err } diff --git a/helper/salt/salt.go b/helper/salt/salt.go index 24fd208bd547..450d9c6e7360 100644 --- a/helper/salt/salt.go +++ b/helper/salt/salt.go @@ -51,7 +51,7 @@ type Config struct { } // NewSalt creates a new salt based on the configuration -func NewSalt(view logical.Storage, config *Config) (*Salt, error) { +func NewSalt(ctx context.Context, view logical.Storage, config *Config) (*Salt, error) { // Setup the configuration if config == nil { config = &Config{} @@ -76,7 +76,7 @@ func NewSalt(view logical.Storage, config *Config) (*Salt, error) { var raw *logical.StorageEntry var err error if view != nil { - raw, err = view.Get(context.Background(), config.Location) + raw, err = view.Get(ctx, config.Location) if err != nil { return nil, fmt.Errorf("failed to read salt: %v", err) } @@ -99,7 +99,7 @@ func NewSalt(view logical.Storage, config *Config) (*Salt, error) { Key: config.Location, Value: []byte(s.salt), } - if err := view.Put(context.Background(), raw); err != nil { + if err := view.Put(ctx, raw); err != nil { return nil, fmt.Errorf("failed to persist salt: %v", err) } } diff --git a/helper/salt/salt_test.go b/helper/salt/salt_test.go index 2470dc8e2094..25359c08d1e7 100644 --- a/helper/salt/salt_test.go +++ b/helper/salt/salt_test.go @@ -14,7 +14,7 @@ func TestSalt(t *testing.T) { inm := &logical.InmemStorage{} conf := &Config{} - salt, err := NewSalt(inm, conf) + salt, err := NewSalt(context.Background(), inm, conf) if err != nil { t.Fatalf("err: %v", err) } @@ -33,7 +33,7 @@ func TestSalt(t *testing.T) { } // Create a new salt, should restore - salt2, err := NewSalt(inm, conf) + salt2, err := NewSalt(context.Background(), inm, conf) if err != nil { t.Fatalf("err: %v", err) } diff --git a/logical/framework/path_map.go b/logical/framework/path_map.go index 6e369e24a3f5..83aa0bafaa1f 100644 --- a/logical/framework/path_map.go +++ b/logical/framework/path_map.go @@ -22,7 +22,7 @@ type PathMap struct { Schema map[string]*FieldSchema CaseSensitive bool Salt *saltpkg.Salt - SaltFunc func() (*saltpkg.Salt, error) + SaltFunc func(context.Context) (*saltpkg.Salt, error) once sync.Once } @@ -58,7 +58,7 @@ func (p *PathMap) pathStruct(ctx context.Context, s logical.Storage, k string) ( salt := p.Salt var err error if p.SaltFunc != nil { - salt, err = p.SaltFunc() + salt, err = p.SaltFunc(ctx) if err != nil { return nil, err } diff --git a/logical/framework/path_map_test.go b/logical/framework/path_map_test.go index b1cce0923f2c..97ab774aee3d 100644 --- a/logical/framework/path_map_test.go +++ b/logical/framework/path_map_test.go @@ -143,7 +143,7 @@ func TestPathMap_routes(t *testing.T) { func TestPathMap_Salted(t *testing.T) { storage := new(logical.InmemStorage) - salt, err := saltpkg.NewSalt(storage, &saltpkg.Config{ + salt, err := saltpkg.NewSalt(context.Background(), storage, &saltpkg.Config{ HashFunc: saltpkg.SHA1Hash, }) if err != nil { @@ -335,14 +335,14 @@ func testSalting(t *testing.T, ctx context.Context, storage logical.Storage, sal func TestPathMap_SaltFunc(t *testing.T) { storage := new(logical.InmemStorage) - salt, err := saltpkg.NewSalt(storage, &saltpkg.Config{ + salt, err := saltpkg.NewSalt(context.Background(), storage, &saltpkg.Config{ HashFunc: saltpkg.SHA1Hash, }) if err != nil { t.Fatalf("err: %v", err) } - saltFunc := func() (*saltpkg.Salt, error) { + saltFunc := func(context.Context) (*saltpkg.Salt, error) { return salt, nil } diff --git a/vault/audit_broker.go b/vault/audit_broker.go index 3584f8a6f625..d39856d8d6dd 100644 --- a/vault/audit_broker.go +++ b/vault/audit_broker.go @@ -60,7 +60,7 @@ func (a *AuditBroker) IsRegistered(name string) bool { } // GetHash returns a hash using the salt of the given backend -func (a *AuditBroker) GetHash(name string, input string) (string, error) { +func (a *AuditBroker) GetHash(ctx context.Context, name string, input string) (string, error) { a.RLock() defer a.RUnlock() be, ok := a.backends[name] @@ -68,7 +68,7 @@ func (a *AuditBroker) GetHash(name string, input string) (string, error) { return "", fmt.Errorf("unknown audit backend %s", name) } - return be.backend.GetHash(input) + return be.backend.GetHash(ctx, input) } // LogRequest is used to ensure all the audit backends have an opportunity to @@ -110,7 +110,7 @@ func (a *AuditBroker) LogRequest(ctx context.Context, in *audit.LogInput, header anyLogged := false for name, be := range a.backends { in.Request.Headers = nil - transHeaders, thErr := headersConfig.ApplyConfig(headers, be.backend.GetHash) + transHeaders, thErr := headersConfig.ApplyConfig(ctx, headers, be.backend.GetHash) if thErr != nil { a.logger.Error("audit: backend failed to include headers", "backend", name, "error", thErr) continue @@ -166,7 +166,7 @@ func (a *AuditBroker) LogResponse(ctx context.Context, in *audit.LogInput, heade anyLogged := false for name, be := range a.backends { in.Request.Headers = nil - transHeaders, thErr := headersConfig.ApplyConfig(headers, be.backend.GetHash) + transHeaders, thErr := headersConfig.ApplyConfig(ctx, headers, be.backend.GetHash) if thErr != nil { a.logger.Error("audit: backend failed to include headers", "backend", name, "error", thErr) continue diff --git a/vault/audit_test.go b/vault/audit_test.go index 8ea249ce8b4e..820766b894d7 100644 --- a/vault/audit_test.go +++ b/vault/audit_test.go @@ -66,7 +66,7 @@ func (n *NoopAudit) LogResponse(ctx context.Context, in *audit.LogInput) error { return n.RespErr } -func (n *NoopAudit) Salt() (*salt.Salt, error) { +func (n *NoopAudit) Salt(ctx context.Context) (*salt.Salt, error) { n.saltMutex.RLock() if n.salt != nil { defer n.saltMutex.RUnlock() @@ -78,7 +78,7 @@ func (n *NoopAudit) Salt() (*salt.Salt, error) { if n.salt != nil { return n.salt, nil } - salt, err := salt.NewSalt(n.Config.SaltView, n.Config.SaltConfig) + salt, err := salt.NewSalt(ctx, n.Config.SaltView, n.Config.SaltConfig) if err != nil { return nil, err } @@ -86,8 +86,8 @@ func (n *NoopAudit) Salt() (*salt.Salt, error) { return salt, nil } -func (n *NoopAudit) GetHash(data string) (string, error) { - salt, err := n.Salt() +func (n *NoopAudit) GetHash(ctx context.Context, data string) (string, error) { + salt, err := n.Salt(ctx) if err != nil { return "", err } diff --git a/vault/audited_headers.go b/vault/audited_headers.go index 46e85b0e7462..ce5c2b1b3d14 100644 --- a/vault/audited_headers.go +++ b/vault/audited_headers.go @@ -89,7 +89,7 @@ func (a *AuditedHeadersConfig) remove(ctx context.Context, header string) error // ApplyConfig returns a map of approved headers and their values, either // hmac'ed or plaintext -func (a *AuditedHeadersConfig) ApplyConfig(headers map[string][]string, hashFunc func(string) (string, error)) (result map[string][]string, retErr error) { +func (a *AuditedHeadersConfig) ApplyConfig(ctx context.Context, headers map[string][]string, hashFunc func(context.Context, string) (string, error)) (result map[string][]string, retErr error) { // Grab a read lock a.RLock() defer a.RUnlock() @@ -111,7 +111,7 @@ func (a *AuditedHeadersConfig) ApplyConfig(headers map[string][]string, hashFunc // Optionally hmac the values if settings.HMAC { for i, el := range hVals { - hVal, err := hashFunc(el) + hVal, err := hashFunc(ctx, el) if err != nil { return nil, err } diff --git a/vault/audited_headers_test.go b/vault/audited_headers_test.go index a673ba0bc88a..473b0bb7c8da 100644 --- a/vault/audited_headers_test.go +++ b/vault/audited_headers_test.go @@ -167,9 +167,9 @@ func TestAuditedHeadersConfig_ApplyConfig(t *testing.T) { "Content-Type": []string{"json"}, } - hashFunc := func(s string) (string, error) { return "hashed", nil } + hashFunc := func(ctx context.Context, s string) (string, error) { return "hashed", nil } - result, err := conf.ApplyConfig(reqHeaders, hashFunc) + result, err := conf.ApplyConfig(context.Background(), reqHeaders, hashFunc) if err != nil { t.Fatal(err) } @@ -213,16 +213,16 @@ func BenchmarkAuditedHeaderConfig_ApplyConfig(b *testing.B) { "Content-Type": []string{"json"}, } - salter, err := salt.NewSalt(nil, nil) + salter, err := salt.NewSalt(context.Background(), nil, nil) if err != nil { b.Fatal(err) } - hashFunc := func(s string) (string, error) { return salter.GetIdentifiedHMAC(s), nil } + hashFunc := func(ctx context.Context, s string) (string, error) { return salter.GetIdentifiedHMAC(s), nil } // Reset the timer since we did a lot above b.ResetTimer() for i := 0; i < b.N; i++ { - conf.ApplyConfig(reqHeaders, hashFunc) + conf.ApplyConfig(context.Background(), reqHeaders, hashFunc) } } diff --git a/vault/expiration.go b/vault/expiration.go index 006ff001df0e..16294c4c2a3a 100644 --- a/vault/expiration.go +++ b/vault/expiration.go @@ -219,7 +219,7 @@ func (m *ExpirationManager) Tidy() error { isValid, ok = tokenCache[le.ClientToken] if !ok { - saltedID, err := m.tokenStore.SaltID(le.ClientToken) + saltedID, err := m.tokenStore.SaltID(m.quitContext, le.ClientToken) if err != nil { tidyErrors = multierror.Append(tidyErrors, fmt.Errorf("failed to lookup salt id: %v", err)) return @@ -563,7 +563,7 @@ func (m *ExpirationManager) RevokeByToken(te *TokenEntry) error { } if te.Path != "" { - saltedID, err := m.tokenStore.SaltID(te.ID) + saltedID, err := m.tokenStore.SaltID(m.quitContext, te.ID) if err != nil { return err } @@ -715,7 +715,7 @@ func (m *ExpirationManager) RenewToken(req *logical.Request, source string, toke defer metrics.MeasureSince([]string{"expire", "renew-token"}, time.Now()) // Compute the Lease ID - saltedID, err := m.tokenStore.SaltID(token) + saltedID, err := m.tokenStore.SaltID(m.quitContext, token) if err != nil { return nil, err } @@ -891,7 +891,7 @@ func (m *ExpirationManager) RegisterAuth(source string, auth *logical.Auth) erro return fmt.Errorf("expiration: %s", consts.ErrPathContainsParentReferences) } - saltedID, err := m.tokenStore.SaltID(auth.ClientToken) + saltedID, err := m.tokenStore.SaltID(m.quitContext, auth.ClientToken) if err != nil { return err } @@ -928,7 +928,7 @@ func (m *ExpirationManager) FetchLeaseTimesByToken(source, token string) (*lease defer metrics.MeasureSince([]string{"expire", "fetch-lease-times-by-token"}, time.Now()) // Compute the Lease ID - saltedID, err := m.tokenStore.SaltID(token) + saltedID, err := m.tokenStore.SaltID(m.quitContext, token) if err != nil { return nil, err } @@ -1180,12 +1180,12 @@ func (m *ExpirationManager) deleteEntry(leaseID string) error { // createIndexByToken creates a secondary index from the token to a lease entry func (m *ExpirationManager) createIndexByToken(token, leaseID string) error { - saltedID, err := m.tokenStore.SaltID(token) + saltedID, err := m.tokenStore.SaltID(m.quitContext, token) if err != nil { return err } - leaseSaltedID, err := m.tokenStore.SaltID(leaseID) + leaseSaltedID, err := m.tokenStore.SaltID(m.quitContext, leaseID) if err != nil { return err } @@ -1202,12 +1202,12 @@ func (m *ExpirationManager) createIndexByToken(token, leaseID string) error { // indexByToken looks up the secondary index from the token to a lease entry func (m *ExpirationManager) indexByToken(token, leaseID string) (*logical.StorageEntry, error) { - saltedID, err := m.tokenStore.SaltID(token) + saltedID, err := m.tokenStore.SaltID(m.quitContext, token) if err != nil { return nil, err } - leaseSaltedID, err := m.tokenStore.SaltID(leaseID) + leaseSaltedID, err := m.tokenStore.SaltID(m.quitContext, leaseID) if err != nil { return nil, err } @@ -1222,12 +1222,12 @@ func (m *ExpirationManager) indexByToken(token, leaseID string) (*logical.Storag // removeIndexByToken removes the secondary index from the token to a lease entry func (m *ExpirationManager) removeIndexByToken(token, leaseID string) error { - saltedID, err := m.tokenStore.SaltID(token) + saltedID, err := m.tokenStore.SaltID(m.quitContext, token) if err != nil { return err } - leaseSaltedID, err := m.tokenStore.SaltID(leaseID) + leaseSaltedID, err := m.tokenStore.SaltID(m.quitContext, leaseID) if err != nil { return err } @@ -1241,7 +1241,7 @@ func (m *ExpirationManager) removeIndexByToken(token, leaseID string) error { // lookupByToken is used to lookup all the leaseID's via the func (m *ExpirationManager) lookupByToken(token string) ([]string, error) { - saltedID, err := m.tokenStore.SaltID(token) + saltedID, err := m.tokenStore.SaltID(m.quitContext, token) if err != nil { return nil, err } diff --git a/vault/logical_system.go b/vault/logical_system.go index 508747415622..f2915065f20e 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -2387,7 +2387,7 @@ func (b *SystemBackend) handleAuditHash(ctx context.Context, req *logical.Reques path = sanitizeMountPath(path) - hash, err := b.Core.auditBroker.GetHash(path, input) + hash, err := b.Core.auditBroker.GetHash(ctx, path, input) if err != nil { return logical.ErrorResponse(err.Error()), nil } diff --git a/vault/router.go b/vault/router.go index d02204f73d69..c3dae498ace6 100644 --- a/vault/router.go +++ b/vault/router.go @@ -19,7 +19,7 @@ type Router struct { root *radix.Tree mountUUIDCache *radix.Tree mountAccessorCache *radix.Tree - tokenStoreSaltFunc func() (*salt.Salt, error) + tokenStoreSaltFunc func(context.Context) (*salt.Salt, error) // storagePrefix maps the prefix used for storage (ala the BarrierView) // to the backend. This is used to map a key back into the backend that owns it. // For example, logical/uuid1/foobar -> secrets/ (kv backend) + foobar @@ -447,7 +447,7 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc case strings.HasPrefix(originalPath, "cubbyhole/"): // In order for the token store to revoke later, we need to have the same // salted ID, so we double-salt what's going to the cubbyhole backend - salt, err := r.tokenStoreSaltFunc() + salt, err := r.tokenStoreSaltFunc(ctx) if err != nil { return nil, false, false, err } diff --git a/vault/testing.go b/vault/testing.go index b91b581f4add..2bbd91950bc8 100644 --- a/vault/testing.go +++ b/vault/testing.go @@ -603,8 +603,8 @@ type noopAudit struct { saltMutex sync.RWMutex } -func (n *noopAudit) GetHash(data string) (string, error) { - salt, err := n.Salt() +func (n *noopAudit) GetHash(ctx context.Context, data string) (string, error) { + salt, err := n.Salt(ctx) if err != nil { return "", err } @@ -629,7 +629,7 @@ func (n *noopAudit) Invalidate(_ context.Context) { n.salt = nil } -func (n *noopAudit) Salt() (*salt.Salt, error) { +func (n *noopAudit) Salt(ctx context.Context) (*salt.Salt, error) { n.saltMutex.RLock() if n.salt != nil { defer n.saltMutex.RUnlock() @@ -641,7 +641,7 @@ func (n *noopAudit) Salt() (*salt.Salt, error) { if n.salt != nil { return n.salt, nil } - salt, err := salt.NewSalt(n.Config.SaltView, n.Config.SaltConfig) + salt, err := salt.NewSalt(ctx, n.Config.SaltView, n.Config.SaltConfig) if err != nil { return nil, err } diff --git a/vault/token_store.go b/vault/token_store.go index c65b9a3cabe3..51dbf3e645f8 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -491,7 +491,7 @@ func (ts *TokenStore) Invalidate(ctx context.Context, key string) { } } -func (ts *TokenStore) Salt() (*salt.Salt, error) { +func (ts *TokenStore) Salt(ctx context.Context) (*salt.Salt, error) { ts.saltLock.RLock() if ts.salt != nil { defer ts.saltLock.RUnlock() @@ -503,7 +503,7 @@ func (ts *TokenStore) Salt() (*salt.Salt, error) { if ts.salt != nil { return ts.salt, nil } - salt, err := salt.NewSalt(ts.view, &salt.Config{ + salt, err := salt.NewSalt(ctx, ts.view, &salt.Config{ HashFunc: salt.SHA1Hash, Location: salt.DefaultLocation, }) @@ -667,8 +667,8 @@ func (ts *TokenStore) SetExpirationManager(exp *ExpirationManager) { } // SaltID is used to apply a salt and hash to an ID to make sure its not reversible -func (ts *TokenStore) SaltID(id string) (string, error) { - s, err := ts.Salt() +func (ts *TokenStore) SaltID(ctx context.Context, id string) (string, error) { + s, err := ts.Salt(ctx) if err != nil { return "", err } @@ -731,7 +731,7 @@ func (ts *TokenStore) createAccessor(ctx context.Context, entry *TokenEntry) err entry.Accessor = accessorUUID // Create index entry, mapping the accessor to the token ID - saltID, err := ts.SaltID(entry.Accessor) + saltID, err := ts.SaltID(ctx, entry.Accessor) if err != nil { return err } @@ -766,7 +766,7 @@ func (ts *TokenStore) create(ctx context.Context, entry *TokenEntry) error { entry.ID = entryUUID } - saltedId, err := ts.SaltID(entry.ID) + saltedId, err := ts.SaltID(ctx, entry.ID) if err != nil { return err } @@ -795,7 +795,7 @@ func (ts *TokenStore) store(ctx context.Context, entry *TokenEntry) error { // storeCommon handles the actual storage of an entry, possibly generating // secondary indexes func (ts *TokenStore) storeCommon(ctx context.Context, entry *TokenEntry, writeSecondary bool) error { - saltedId, err := ts.SaltID(entry.ID) + saltedId, err := ts.SaltID(ctx, entry.ID) if err != nil { return err } @@ -822,7 +822,7 @@ func (ts *TokenStore) storeCommon(ctx context.Context, entry *TokenEntry, writeS } // Create the index entry - parentSaltedID, err := ts.SaltID(entry.Parent) + parentSaltedID, err := ts.SaltID(ctx, entry.Parent) if err != nil { return err } @@ -875,7 +875,7 @@ func (ts *TokenStore) UseToken(ctx context.Context, te *TokenEntry) (*TokenEntry defer lock.Unlock() // Call lookupSalted instead of Lookup to avoid deadlocking since Lookup grabs a read lock - saltedID, err := ts.SaltID(te.ID) + saltedID, err := ts.SaltID(ctx, te.ID) if err != nil { return nil, err } @@ -932,7 +932,7 @@ func (ts *TokenStore) Lookup(ctx context.Context, id string) (*TokenEntry, error lock.RLock() defer lock.RUnlock() - saltedID, err := ts.SaltID(id) + saltedID, err := ts.SaltID(ctx, id) if err != nil { return nil, err } @@ -951,7 +951,7 @@ func (ts *TokenStore) lookupTainted(ctx context.Context, id string) (*TokenEntry lock.RLock() defer lock.RUnlock() - saltedID, err := ts.SaltID(id) + saltedID, err := ts.SaltID(ctx, id) if err != nil { return nil, err } @@ -1051,7 +1051,7 @@ func (ts *TokenStore) Revoke(ctx context.Context, id string) error { return fmt.Errorf("cannot revoke blank token") } - saltedID, err := ts.SaltID(id) + saltedID, err := ts.SaltID(ctx, id) if err != nil { return err } @@ -1145,7 +1145,7 @@ func (ts *TokenStore) revokeSalted(ctx context.Context, saltedId string) (ret er // Clear the secondary index if any if entry.Parent != "" { - parentSaltedID, err := ts.SaltID(entry.Parent) + parentSaltedID, err := ts.SaltID(ctx, entry.Parent) if err != nil { return err } @@ -1158,7 +1158,7 @@ func (ts *TokenStore) revokeSalted(ctx context.Context, saltedId string) (ret er // Clear the accessor index if any if entry.Accessor != "" { - accessorSaltedID, err := ts.SaltID(entry.Accessor) + accessorSaltedID, err := ts.SaltID(ctx, entry.Accessor) if err != nil { return err } @@ -1188,7 +1188,7 @@ func (ts *TokenStore) RevokeTree(ctx context.Context, id string) error { } // Get the salted ID - saltedId, err := ts.SaltID(id) + saltedId, err := ts.SaltID(ctx, id) if err != nil { return err } @@ -1251,7 +1251,7 @@ func (ts *TokenStore) handleCreateAgainstRole(ctx context.Context, req *logical. } func (ts *TokenStore) lookupByAccessor(ctx context.Context, accessor string, tainted bool) (accessorEntry, error) { - saltedID, err := ts.SaltID(accessor) + saltedID, err := ts.SaltID(ctx, accessor) if err != nil { return accessorEntry{}, err } @@ -1272,7 +1272,7 @@ func (ts *TokenStore) lookupBySaltedAccessor(ctx context.Context, saltedAccessor err = jsonutil.DecodeJSON(entry.Value, &aEntry) // If we hit an error, assume it's a pre-struct straight token ID if err != nil { - saltedID, err := ts.SaltID(string(entry.Value)) + saltedID, err := ts.SaltID(ctx, string(entry.Value)) if err != nil { return accessorEntry{}, err } @@ -1395,7 +1395,7 @@ func (ts *TokenStore) handleTidy(ctx context.Context, req *logical.Request, data // Look up tainted variants so we only find entries that truly don't // exist - saltedId, err := ts.SaltID(accessorEntry.TokenID) + saltedId, err := ts.SaltID(ctx, accessorEntry.TokenID) if err != nil { tidyErrors = multierror.Append(tidyErrors, fmt.Errorf("failed to read salt id: %v", err)) lock.RUnlock() @@ -2086,7 +2086,7 @@ func (ts *TokenStore) handleLookup(ctx context.Context, req *logical.Request, da defer lock.RUnlock() // Lookup the token - saltedId, err := ts.SaltID(id) + saltedId, err := ts.SaltID(ctx, id) if err != nil { return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest } diff --git a/vault/token_store_test.go b/vault/token_store_test.go index fa865c7e2199..c2396a2fd57f 100644 --- a/vault/token_store_test.go +++ b/vault/token_store_test.go @@ -58,7 +58,7 @@ func TestTokenStore_TokenEntryUpgrade(t *testing.T) { t.Fatal(err) } - saltedId, err := ts.SaltID(entry.ID) + saltedId, err := ts.SaltID(context.Background(), entry.ID) if err != nil { t.Fatal(err) } @@ -304,7 +304,7 @@ func TestTokenStore_HandleRequest_ListAccessors(t *testing.T) { } // Revoke root to make the number of accessors match - salted, err := ts.SaltID(root) + salted, err := ts.SaltID(context.Background(), root) if err != nil { t.Fatal(err) } @@ -339,7 +339,7 @@ func TestTokenStore_HandleRequest_ListAccessors(t *testing.T) { if aEntry.TokenID == "" || aEntry.AccessorID == "" { t.Fatalf("error, accessor entry looked up is empty, but no error thrown") } - salted, err := ts.SaltID(accessor) + salted, err := ts.SaltID(context.Background(), accessor) if err != nil { t.Fatal(err) } @@ -522,7 +522,7 @@ func TestTokenStore_CreateLookup_ExpirationInRestoreMode(t *testing.T) { } // Replace the lease with a lease with an expire time in the past - saltedID, err := ts.SaltID(ent.ID) + saltedID, err := ts.SaltID(context.Background(), ent.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -3254,7 +3254,7 @@ func TestTokenStore_RevokeUseCountToken(t *testing.T) { } tut := resp.Auth.ClientToken - saltTut, err := ts.SaltID(tut) + saltTut, err := ts.SaltID(context.Background(), tut) if err != nil { t.Fatal(err) } @@ -3452,7 +3452,7 @@ func TestTokenStore_HandleTidyCase1(t *testing.T) { // cubbyhole and by not deleting its secondary index, its accessor and // associated leases. - saltedTut, err := ts.SaltID(tut) + saltedTut, err := ts.SaltID(context.Background(), tut) if err != nil { t.Fatal(err) } @@ -3594,7 +3594,7 @@ func TestTokenStore_TidyLeaseRevocation(t *testing.T) { } // Now, delete the token entry. The leases should still exist. - saltedTut, err := ts.SaltID(tut) + saltedTut, err := ts.SaltID(context.Background(), tut) if err != nil { t.Fatal(err) } From 0fec3965e1ddd8ae5bb244614cf62884e1696f0d Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 8 Mar 2018 15:47:47 -0500 Subject: [PATCH 15/45] Update text around default policy to make it clear that it is user-modifiable --- website/source/docs/concepts/policies.html.md | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/website/source/docs/concepts/policies.html.md b/website/source/docs/concepts/policies.html.md index eaffadb2e126..6573db38dcd4 100644 --- a/website/source/docs/concepts/policies.html.md +++ b/website/source/docs/concepts/policies.html.md @@ -368,9 +368,9 @@ wrapping mandatory for a particular path. wrapped response. If both are specified, the minimum value must be less than the maximum. In -addition, if paths are merged from different stanzas, the lowest value specified -for each is the value that will result, in line with the idea of keeping token -lifetimes as short as possible. +addition, if paths are merged from different stanzas, the lowest value +specified for each is the value that will result, in line with the idea of +keeping token lifetimes as short as possible. ## Builtin Policies @@ -379,10 +379,17 @@ the two builtin policies. ### Default Policy -The `default` policy is a builtin Vault policy that cannot be modified or -removed. By default, it is attached to all tokens, but may be explicitly -detached at creation time. The policy contains basic functionality such as the -ability for the token to lookup data about itself and to use its cubbyhole data. +The `default` policy is a builtin Vault policy that cannot be removed. By +default, it is attached to all tokens, but may be explicitly excluded at token +creation time by supporting authentication methods. + +The policy contains basic functionality such as the ability for the token to +look up data about itself and to use its cubbyhole data. However, Vault is not +proscriptive about its contents. It can be modified to suit your needs; Vault +will never overwrite your modifications. If you want to stay up-to-date with +the latest upstream version of the `default` policy, simply read the contents +of the policy from an up-to-date `dev` server, and write those contents into +your Vault's `default` policy. To view all permissions granted by the default policy on your Vault installation, run: From 14eab5c6665e2777db5e3ec7ca727463b8f4a108 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 8 Mar 2018 16:14:20 -0500 Subject: [PATCH 16/45] Fix output in warning displaying nanoseconds instead of seconds --- vault/expiration.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vault/expiration.go b/vault/expiration.go index 16294c4c2a3a..2388c1c19d0d 100644 --- a/vault/expiration.go +++ b/vault/expiration.go @@ -768,14 +768,14 @@ func (m *ExpirationManager) RenewToken(req *logical.Request, source string, toke // framework.LeaseExtend call against the request. Also, cap period value to // the sys/mount max value. if resp.Auth.Period > sysView.MaxLeaseTTL() { - retResp.AddWarning(fmt.Sprintf("Period of %d seconds is greater than current mount/system default of %d seconds, value will be truncated.", resp.Auth.TTL, sysView.MaxLeaseTTL())) + retResp.AddWarning(fmt.Sprintf("Period of %s is greater than current mount/system default of %s, value will be truncated.", resp.Auth.TTL, sysView.MaxLeaseTTL())) resp.Auth.Period = sysView.MaxLeaseTTL() } resp.Auth.TTL = resp.Auth.Period case resp.Auth.TTL > time.Duration(0): // Cap TTL value to the sys/mount max value if resp.Auth.TTL > sysView.MaxLeaseTTL() { - retResp.AddWarning(fmt.Sprintf("TTL of %d seconds is greater than current mount/system default of %d seconds, value will be truncated.", resp.Auth.TTL, sysView.MaxLeaseTTL())) + retResp.AddWarning(fmt.Sprintf("TTL of %s is greater than current mount/system default of %s, value will be truncated.", resp.Auth.TTL, sysView.MaxLeaseTTL())) resp.Auth.TTL = sysView.MaxLeaseTTL() } } From 1d8baa9b9c6247b83466b7133a1df0213293e3d4 Mon Sep 17 00:00:00 2001 From: Vishal Nayak Date: Thu, 8 Mar 2018 17:49:08 -0500 Subject: [PATCH 17/45] approle: Use TypeCommaStringSlice for BoundCIDRList (#4078) * Use TypeCommaStringSlice for Approle bound_cidr_list * update docs * Add comments in the test --- builtin/credential/approle/path_role.go | 86 +++++++++------ builtin/credential/approle/path_role_test.go | 102 +++++++++++++++++- builtin/credential/approle/validation.go | 18 ++-- helper/cidrutil/cidr.go | 24 ----- helper/cidrutil/cidr_test.go | 44 -------- website/source/api/auth/approle/index.html.md | 23 ++-- 6 files changed, 173 insertions(+), 124 deletions(-) diff --git a/builtin/credential/approle/path_role.go b/builtin/credential/approle/path_role.go index e1d6bfc59d96..08b3ff4f9d6a 100644 --- a/builtin/credential/approle/path_role.go +++ b/builtin/credential/approle/path_role.go @@ -9,6 +9,7 @@ import ( "github.com/fatih/structs" "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/cidrutil" + "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/locksutil" "github.com/hashicorp/vault/helper/policyutil" "github.com/hashicorp/vault/helper/strutil" @@ -50,7 +51,10 @@ type roleStorageEntry struct { BindSecretID bool `json:"bind_secret_id" structs:"bind_secret_id" mapstructure:"bind_secret_id"` // A constraint, if set, specifies the CIDR blocks from which logins should be allowed - BoundCIDRList string `json:"bound_cidr_list" structs:"bound_cidr_list" mapstructure:"bound_cidr_list"` + BoundCIDRListOld string `json:"bound_cidr_list,omitempty"` + + // A constraint, if set, specifies the CIDR blocks from which logins should be allowed + BoundCIDRList []string `json:"bound_cidr_list_list" structs:"bound_cidr_list" mapstructure:"bound_cidr_list"` // Period, if set, indicates that the token generated using this role // should never expire. The token should be renewed within the duration @@ -113,9 +117,9 @@ func rolePaths(b *backend) []*framework.Path { Description: "Impose secret_id to be presented when logging in using this role. Defaults to 'true'.", }, "bound_cidr_list": &framework.FieldSchema{ - Type: framework.TypeString, - Description: `Comma separated list of CIDR blocks, if set, specifies blocks of IP -addresses which can perform the login operation`, + Type: framework.TypeCommaStringSlice, + Description: `Comma separated string or list of CIDR blocks. If set, specifies the blocks of +IP addresses which can perform the login operation.`, }, "policies": &framework.FieldSchema{ Type: framework.TypeCommaStringSlice, @@ -198,9 +202,9 @@ TTL will be set to the value of this parameter.`, Description: "Name of the role.", }, "bound_cidr_list": &framework.FieldSchema{ - Type: framework.TypeString, - Description: `Comma separated list of CIDR blocks, if set, specifies blocks of IP -addresses which can perform the login operation`, + Type: framework.TypeCommaStringSlice, + Description: `Comma separated string or list of CIDR blocks. If set, specifies the blocks of +IP addresses which can perform the login operation.`, }, }, Callbacks: map[logical.Operation]framework.OperationFunc{ @@ -391,8 +395,8 @@ be renewed. Defaults to 0, in which case the value will fall back to the system/ formatted string containing the metadata in key value pairs.`, }, "cidr_list": &framework.FieldSchema{ - Type: framework.TypeString, - Description: `Comma separated list of CIDR blocks enforcing secret IDs to be used from + Type: framework.TypeCommaStringSlice, + Description: `Comma separated string or list of CIDR blocks enforcing secret IDs to be used from specific set of IP addresses. If 'bound_cidr_list' is set on the role, then the list of CIDR blocks listed here should be a subset of the CIDR blocks listed on the role.`, @@ -496,8 +500,8 @@ the role.`, formatted string containing metadata in key value pairs.`, }, "cidr_list": &framework.FieldSchema{ - Type: framework.TypeString, - Description: `Comma separated list of CIDR blocks enforcing secret IDs to be used from + Type: framework.TypeCommaStringSlice, + Description: `Comma separated string or list of CIDR blocks enforcing secret IDs to be used from specific set of IP addresses. If 'bound_cidr_list' is set on the role, then the list of CIDR blocks listed here should be a subset of the CIDR blocks listed on the role.`, @@ -633,7 +637,7 @@ func validateRoleConstraints(role *roleStorageEntry) error { // At least one constraint should be enabled on the role switch { case role.BindSecretID: - case role.BoundCIDRList != "": + case len(role.BoundCIDRList) != 0: default: return fmt.Errorf("at least one constraint should be enabled on the role") } @@ -718,6 +722,27 @@ func (b *backend) roleEntry(ctx context.Context, s logical.Storage, roleName str return nil, err } + needsUpgrade := false + + if role.BoundCIDRListOld != "" { + role.BoundCIDRList = strings.Split(role.BoundCIDRListOld, ",") + role.BoundCIDRListOld = "" + needsUpgrade = true + } + + if needsUpgrade && (b.System().LocalMount() || !b.System().ReplicationState().HasState(consts.ReplicationPerformanceSecondary)) { + entry, err := logical.StorageEntryJSON("role/"+strings.ToLower(roleName), &role) + if err != nil { + return nil, err + } + if err := s.Put(ctx, entry); err != nil { + // Only perform upgrades on replication primary + if !strings.Contains(err.Error(), logical.ErrReadOnly.Error()) { + return nil, err + } + } + } + return &role, nil } @@ -774,13 +799,13 @@ func (b *backend) pathRoleCreateUpdate(ctx context.Context, req *logical.Request } if boundCIDRListRaw, ok := data.GetOk("bound_cidr_list"); ok { - role.BoundCIDRList = strings.TrimSpace(boundCIDRListRaw.(string)) + role.BoundCIDRList = boundCIDRListRaw.([]string) } else if req.Operation == logical.CreateOperation { - role.BoundCIDRList = data.Get("bound_cidr_list").(string) + role.BoundCIDRList = data.Get("bound_cidr_list").([]string) } - if role.BoundCIDRList != "" { - valid, err := cidrutil.ValidateCIDRListString(role.BoundCIDRList, ",") + if len(role.BoundCIDRList) != 0 { + valid, err := cidrutil.ValidateCIDRListSlice(role.BoundCIDRList) if err != nil { return nil, fmt.Errorf("failed to validate CIDR blocks: %v", err) } @@ -1242,19 +1267,17 @@ func (b *backend) pathRoleBoundCIDRListUpdate(ctx context.Context, req *logical. return nil, nil } - role.BoundCIDRList = strings.TrimSpace(data.Get("bound_cidr_list").(string)) - if role.BoundCIDRList == "" { + role.BoundCIDRList = data.Get("bound_cidr_list").([]string) + if len(role.BoundCIDRList) == 0 { return logical.ErrorResponse("missing bound_cidr_list"), nil } - if role.BoundCIDRList != "" { - valid, err := cidrutil.ValidateCIDRListString(role.BoundCIDRList, ",") - if err != nil { - return nil, fmt.Errorf("failed to validate CIDR blocks: %v", err) - } - if !valid { - return logical.ErrorResponse("failed to validate CIDR blocks"), nil - } + valid, err := cidrutil.ValidateCIDRListSlice(role.BoundCIDRList) + if err != nil { + return nil, fmt.Errorf("failed to validate CIDR blocks: %v", err) + } + if !valid { + return logical.ErrorResponse("failed to validate CIDR blocks"), nil } return nil, b.setRoleEntry(ctx, req.Storage, roleName, role, "") @@ -1302,7 +1325,7 @@ func (b *backend) pathRoleBoundCIDRListDelete(ctx context.Context, req *logical. } // Deleting a field implies setting the value to it's default value. - role.BoundCIDRList = data.GetDefaultOrZero("bound_cidr_list").(string) + role.BoundCIDRList = data.GetDefaultOrZero("bound_cidr_list").([]string) return nil, b.setRoleEntry(ctx, req.Storage, roleName, role, "") } @@ -1990,11 +2013,11 @@ func (b *backend) handleRoleSecretIDCommon(ctx context.Context, req *logical.Req return logical.ErrorResponse("bind_secret_id is not set on the role"), nil } - cidrList := data.Get("cidr_list").(string) + secretIDCIDRs := data.Get("cidr_list").([]string) // Validate the list of CIDR blocks - if cidrList != "" { - valid, err := cidrutil.ValidateCIDRListString(cidrList, ",") + if len(secretIDCIDRs) != 0 { + valid, err := cidrutil.ValidateCIDRListSlice(secretIDCIDRs) if err != nil { return nil, fmt.Errorf("failed to validate CIDR blocks: %v", err) } @@ -2003,9 +2026,6 @@ func (b *backend) handleRoleSecretIDCommon(ctx context.Context, req *logical.Req } } - // Parse the CIDR blocks into a slice - secretIDCIDRs := strutil.ParseDedupLowercaseAndSortStrings(cidrList, ",") - // Ensure that the CIDRs on the secret ID are a subset of that of role's if err := verifyCIDRRoleSecretIDSubset(secretIDCIDRs, role.BoundCIDRList); err != nil { return nil, err diff --git a/builtin/credential/approle/path_role_test.go b/builtin/credential/approle/path_role_test.go index 65c2ecb95273..1e07c32484fe 100644 --- a/builtin/credential/approle/path_role_test.go +++ b/builtin/credential/approle/path_role_test.go @@ -12,6 +12,105 @@ import ( "github.com/mitchellh/mapstructure" ) +func TestApprole_UpgradeBoundCIDRList(t *testing.T) { + var resp *logical.Response + var err error + + b, storage := createBackendWithStorage(t) + + roleData := map[string]interface{}{ + "policies": []string{"default"}, + "bind_secret_id": true, + "bound_cidr_list": []string{"127.0.0.1/18", "192.178.1.2/24"}, + } + + // Create a role with bound_cidr_list set + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Path: "role/testrole", + Operation: logical.CreateOperation, + Storage: storage, + Data: roleData, + }) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("bad: err: %v\nresp: %#v", err, resp) + } + + // Read the role and check that the bound_cidr_list is set properly + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Path: "role/testrole", + Operation: logical.ReadOperation, + Storage: storage, + }) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("bad: err: %v\nresp: %#v", err, resp) + } + + expected := []string{"127.0.0.1/18", "192.178.1.2/24"} + actual := resp.Data["bound_cidr_list"].([]string) + + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("bad: bound_cidr_list; expected: %#v\nactual: %#v\n", expected, actual) + } + + // Modify the storage entry of the role to hold the old style string typed bound_cidr_list + role := &roleStorageEntry{ + RoleID: "testroleid", + HMACKey: "testhmackey", + Policies: []string{"default"}, + BindSecretID: true, + BoundCIDRListOld: "127.0.0.1/18,192.178.1.2/24", + } + err = b.setRoleEntry(context.Background(), storage, "testrole", role, "") + if err != nil { + t.Fatal(err) + } + + // Read the role. The upgrade code should have migrated the old type to the new type + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Path: "role/testrole", + Operation: logical.ReadOperation, + Storage: storage, + }) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("bad: err: %v\nresp: %#v", err, resp) + } + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("bad: bound_cidr_list; expected: %#v\nactual: %#v\n", expected, actual) + } + + // Create a secret-id by supplying a subset of the role's CIDR blocks with the new type + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Path: "role/testrole/secret-id", + Operation: logical.UpdateOperation, + Storage: storage, + Data: map[string]interface{}{ + "cidr_list": []string{"127.0.0.1/24"}, + }, + }) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("bad: err: %v\nresp: %#v", err, resp) + } + if resp.Data["secret_id"].(string) == "" { + t.Fatalf("failed to generate secret-id") + } + + // Check that the backwards compatibility for the string type is not broken + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Path: "role/testrole/secret-id", + Operation: logical.UpdateOperation, + Storage: storage, + Data: map[string]interface{}{ + "cidr_list": "127.0.0.1/24", + }, + }) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("bad: err: %v\nresp: %#v", err, resp) + } + if resp.Data["secret_id"].(string) == "" { + t.Fatalf("failed to generate secret-id") + } +} + func TestApprole_RoleNameLowerCasing(t *testing.T) { var resp *logical.Response var err error @@ -858,8 +957,9 @@ func TestAppRole_RoleCRUD(t *testing.T) { "token_ttl": 400, "token_max_ttl": 500, "token_num_uses": 600, - "bound_cidr_list": "127.0.0.1/32,127.0.0.1/16", + "bound_cidr_list": []string{"127.0.0.1/32", "127.0.0.1/16"}, } + var expectedStruct roleStorageEntry err = mapstructure.Decode(expected, &expectedStruct) if err != nil { diff --git a/builtin/credential/approle/validation.go b/builtin/credential/approle/validation.go index 9a1fdea95a90..559e14140c62 100644 --- a/builtin/credential/approle/validation.go +++ b/builtin/credential/approle/validation.go @@ -13,7 +13,6 @@ import ( "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/cidrutil" "github.com/hashicorp/vault/helper/locksutil" - "github.com/hashicorp/vault/helper/strutil" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" ) @@ -143,13 +142,13 @@ func (b *backend) validateCredentials(ctx context.Context, req *logical.Request, } } - if role.BoundCIDRList != "" { + if len(role.BoundCIDRList) != 0 { // If 'bound_cidr_list' was set, verify the CIDR restrictions if req.Connection == nil || req.Connection.RemoteAddr == "" { return nil, "", metadata, "", fmt.Errorf("failed to get connection information"), nil } - belongs, err := cidrutil.IPBelongsToCIDRBlocksString(req.Connection.RemoteAddr, role.BoundCIDRList, ",") + belongs, err := cidrutil.IPBelongsToCIDRBlocksSlice(req.Connection.RemoteAddr, role.BoundCIDRList) if err != nil { return nil, "", metadata, "", nil, errwrap.Wrapf("failed to verify the CIDR restrictions set on the role: {{err}}", err) } @@ -163,7 +162,7 @@ func (b *backend) validateCredentials(ctx context.Context, req *logical.Request, // validateBindSecretID is used to determine if the given SecretID is a valid one. func (b *backend) validateBindSecretID(ctx context.Context, req *logical.Request, roleName, secretID, - hmacKey, roleBoundCIDRList string) (bool, map[string]string, error) { + hmacKey string, roleBoundCIDRList []string) (bool, map[string]string, error) { secretIDHMAC, err := createHMAC(hmacKey, secretID) if err != nil { return false, nil, fmt.Errorf("failed to create HMAC of secret_id: %v", err) @@ -281,17 +280,14 @@ func (b *backend) validateBindSecretID(ctx context.Context, req *logical.Request // verifyCIDRRoleSecretIDSubset checks if the CIDR blocks set on the secret ID // are a subset of CIDR blocks set on the role -func verifyCIDRRoleSecretIDSubset(secretIDCIDRs []string, roleBoundCIDRList string) error { +func verifyCIDRRoleSecretIDSubset(secretIDCIDRs []string, roleBoundCIDRList []string) error { if len(secretIDCIDRs) != 0 { - // Parse the CIDRs on role as a slice - roleCIDRs := strutil.ParseDedupLowercaseAndSortStrings(roleBoundCIDRList, ",") - // If there are no CIDR blocks on the role, then the subset // requirement would be satisfied - if len(roleCIDRs) != 0 { - subset, err := cidrutil.SubsetBlocks(roleCIDRs, secretIDCIDRs) + if len(roleBoundCIDRList) != 0 { + subset, err := cidrutil.SubsetBlocks(roleBoundCIDRList, secretIDCIDRs) if !subset || err != nil { - return fmt.Errorf("failed to verify subset relationship between CIDR blocks on the role %q and CIDR blocks on the secret ID %q: %v", roleCIDRs, secretIDCIDRs, err) + return fmt.Errorf("failed to verify subset relationship between CIDR blocks on the role %q and CIDR blocks on the secret ID %q: %v", roleBoundCIDRList, secretIDCIDRs, err) } } } diff --git a/helper/cidrutil/cidr.go b/helper/cidrutil/cidr.go index 8031bb8eb53f..2d89d846864d 100644 --- a/helper/cidrutil/cidr.go +++ b/helper/cidrutil/cidr.go @@ -31,30 +31,6 @@ func IPBelongsToCIDR(ipAddr string, cidr string) (bool, error) { return true, nil } -// IPBelongsToCIDRBlocksString checks if the given IP is encompassed by any of -// the given CIDR blocks, when the input is a string composed by joining all -// the CIDR blocks using a separator. The input is separated based on the given -// separator and the IP is checked to be belonged by any CIDR block. -func IPBelongsToCIDRBlocksString(ipAddr string, cidrList, separator string) (bool, error) { - if ipAddr == "" { - return false, fmt.Errorf("missing IP address") - } - - if cidrList == "" { - return false, fmt.Errorf("missing CIDR list") - } - - if separator == "" { - return false, fmt.Errorf("missing separator") - } - - if ip := net.ParseIP(ipAddr); ip == nil { - return false, fmt.Errorf("invalid IP address") - } - - return IPBelongsToCIDRBlocksSlice(ipAddr, strutil.ParseDedupLowercaseAndSortStrings(cidrList, separator)) -} - // IPBelongsToCIDRBlocksSlice checks if the given IP is encompassed by any of the given // CIDR blocks func IPBelongsToCIDRBlocksSlice(ipAddr string, cidrs []string) (bool, error) { diff --git a/helper/cidrutil/cidr_test.go b/helper/cidrutil/cidr_test.go index f6d5849c3e45..220afecc1ffa 100644 --- a/helper/cidrutil/cidr_test.go +++ b/helper/cidrutil/cidr_test.go @@ -42,50 +42,6 @@ func TestCIDRUtil_IPBelongsToCIDR(t *testing.T) { } } -func TestCIDRUtil_IPBelongsToCIDRBlocksString(t *testing.T) { - ip := "192.168.27.29" - cidrList := "172.169.100.200/18,192.168.0.0/16,10.10.20.20/24" - - belongs, err := IPBelongsToCIDRBlocksString(ip, cidrList, ",") - if err != nil { - t.Fatal(err) - } - if !belongs { - t.Fatalf("expected IP %q to belong to one of the CIDRs in %q", ip, cidrList) - } - - ip = "10.197.192.6" - cidrList = "1.2.3.0/8,10.197.192.0/18,10.197.193.0/24" - - belongs, err = IPBelongsToCIDRBlocksString(ip, cidrList, ",") - if err != nil { - t.Fatal(err) - } - if !belongs { - t.Fatalf("expected IP %q to belong to one of the CIDRs in %q", ip, cidrList) - } - - ip = "192.168.27.29" - cidrList = "172.169.100.200/18,192.168.0.0.0/16,10.10.20.20/24" - - belongs, err = IPBelongsToCIDRBlocksString(ip, cidrList, ",") - if err == nil { - t.Fatalf("expected an error") - } - - ip = "30.40.50.60" - cidrList = "172.169.100.200/18,192.168.0.0/16,10.10.20.20/24" - - belongs, err = IPBelongsToCIDRBlocksString(ip, cidrList, ",") - if err != nil { - t.Fatal(err) - } - if belongs { - t.Fatalf("expected IP %q to not belong to one of the CIDRs in %q", ip, cidrList) - } - -} - func TestCIDRUtil_IPBelongsToCIDRBlocksSlice(t *testing.T) { ip := "192.168.27.29" cidrList := []string{"172.169.100.200/18", "192.168.0.0/16", "10.10.20.20/24"} diff --git a/website/source/api/auth/approle/index.html.md b/website/source/api/auth/approle/index.html.md index b06451b49183..ee7f8ef41f80 100644 --- a/website/source/api/auth/approle/index.html.md +++ b/website/source/api/auth/approle/index.html.md @@ -69,8 +69,9 @@ enabled while creating or updating a role. - `role_name` `(string: )` - Name of the AppRole. - `bind_secret_id` `(bool: true)` - Require `secret_id` to be presented when logging in using this AppRole. -- `bound_cidr_list` `(array: [])` - Comma-separated list of CIDR blocks; if set, - specifies blocks of IP addresses which can perform the login operation. +- `bound_cidr_list` `(array: [])` - Comma-separated string or list of CIDR + blocks; if set, specifies blocks of IP addresses which can perform the login + operation. - `policies` `(array: [])` - Comma-separated list of policies set on tokens issued via this AppRole. - `secret_id_num_uses` `(integer: 0)` - Number of times any particular SecretID @@ -155,7 +156,7 @@ $ curl \ ], "period": 0, "bind_secret_id": true, - "bound_cidr_list": "" + "bound_cidr_list": [] }, "lease_duration": 0, "renewable": false, @@ -285,10 +286,10 @@ itself, and also to delete the SecretID from the AppRole. a JSON-formatted string containing the metadata in key-value pairs. This metadata will be set on tokens issued with this SecretID, and is logged in audit logs _in plaintext_. -- `cidr_list` `(string: "")` - Comma separated list of CIDR blocks enforcing - secret IDs to be used from specific set of IP addresses. If 'bound_cidr_list' - is set on the role, then the list of CIDR blocks listed here should be a - subset of the CIDR blocks listed on the role. +- `cidr_list` `(array: [])` - Comma separated string or list of CIDR blocks + enforcing secret IDs to be used from specific set of IP addresses. If + `bound_cidr_list` is set on the role, then the list of CIDR blocks listed + here should be a subset of the CIDR blocks listed on the role. ### Sample Payload @@ -510,10 +511,10 @@ Assigns a "custom" SecretID against an existing AppRole. This is used in the a JSON-formatted string containing the metadata in key-value pairs. This metadata will be set on tokens issued with this SecretID, and is logged in audit logs _in plaintext_. -- `cidr_list` `(string: "")` - Comma separated list of CIDR blocks enforcing - secret IDs to be used from ppecific set of IP addresses. If 'bound_cidr_list' - is set on the role, then the list of CIDR blocks listed here should be a - subset of the CIDR blocks listed on the role. +- `cidr_list` `(array: [])` - Comma separated string or list of CIDR blocks + enforcing secret IDs to be used from ppecific set of IP addresses. If + `bound_cidr_list` is set on the role, then the list of CIDR blocks listed + here should be a subset of the CIDR blocks listed on the role. ### Sample Payload From 5a89b054e86eaf6bcf05c1d5e918dbd87a4d9ed9 Mon Sep 17 00:00:00 2001 From: Brian Kassouf Date: Thu, 8 Mar 2018 17:58:50 -0800 Subject: [PATCH 18/45] helper/keysutil: Add a storage implementation that uses policies to encrypt paths (#3989) * helper/keysutil: Add a policy encrypted path storage * Add vendored deps * Fix spelling and paths that start with a / * Add a key version template to change configure the ciphertext prefix * Use big.Int for base58 instead of external lib * Update go requirment to 1.10 * Add a version prefix cache * Move logic to helper function * Cache the template parts * Add a storage prefix to policy * Add an error if the policy passed in is nil * Pull in the go1.10 version of the math/big package until we can update --- helper/keysutil/encrypted_key_storage.go | 275 +++ helper/keysutil/encrypted_key_storage_test.go | 358 ++++ helper/keysutil/lock_manager.go | 6 + helper/keysutil/policy.go | 90 +- helper/keysutil/policy_test.go | 1 + vendor/github.com/golang/go/LICENSE | 27 + vendor/github.com/golang/go/PATENTS | 22 + .../golang/go/src/math/big/accuracy_string.go | 17 + .../golang/go/src/math/big/arith.go | 260 +++ .../golang/go/src/math/big/arith_386.s | 271 +++ .../golang/go/src/math/big/arith_amd64.s | 450 +++++ .../golang/go/src/math/big/arith_amd64p32.s | 40 + .../golang/go/src/math/big/arith_arm.s | 294 +++ .../golang/go/src/math/big/arith_arm64.s | 167 ++ .../golang/go/src/math/big/arith_decl.go | 20 + .../golang/go/src/math/big/arith_decl_pure.go | 51 + .../go/src/math/big/arith_decl_s390x.go | 23 + .../golang/go/src/math/big/arith_mips64x.s | 43 + .../golang/go/src/math/big/arith_mipsx.s | 43 + .../golang/go/src/math/big/arith_ppc64x.s | 197 ++ .../golang/go/src/math/big/arith_s390x.s | 1239 ++++++++++++ .../golang/go/src/math/big/decimal.go | 267 +++ .../github.com/golang/go/src/math/big/doc.go | 99 + .../golang/go/src/math/big/float.go | 1717 +++++++++++++++++ .../golang/go/src/math/big/floatconv.go | 293 +++ .../golang/go/src/math/big/floatmarsh.go | 120 ++ .../github.com/golang/go/src/math/big/ftoa.go | 461 +++++ .../github.com/golang/go/src/math/big/int.go | 1033 ++++++++++ .../golang/go/src/math/big/intconv.go | 247 +++ .../golang/go/src/math/big/intmarsh.go | 80 + .../github.com/golang/go/src/math/big/nat.go | 1267 ++++++++++++ .../golang/go/src/math/big/natconv.go | 503 +++++ .../golang/go/src/math/big/prime.go | 320 +++ .../github.com/golang/go/src/math/big/rat.go | 517 +++++ .../golang/go/src/math/big/ratconv.go | 283 +++ .../golang/go/src/math/big/ratmarsh.go | 75 + .../go/src/math/big/roundingmode_string.go | 16 + .../github.com/golang/go/src/math/big/sqrt.go | 151 ++ vendor/vendor.json | 6 + 39 files changed, 11338 insertions(+), 11 deletions(-) create mode 100644 helper/keysutil/encrypted_key_storage.go create mode 100644 helper/keysutil/encrypted_key_storage_test.go create mode 100644 vendor/github.com/golang/go/LICENSE create mode 100644 vendor/github.com/golang/go/PATENTS create mode 100644 vendor/github.com/golang/go/src/math/big/accuracy_string.go create mode 100644 vendor/github.com/golang/go/src/math/big/arith.go create mode 100644 vendor/github.com/golang/go/src/math/big/arith_386.s create mode 100644 vendor/github.com/golang/go/src/math/big/arith_amd64.s create mode 100644 vendor/github.com/golang/go/src/math/big/arith_amd64p32.s create mode 100644 vendor/github.com/golang/go/src/math/big/arith_arm.s create mode 100644 vendor/github.com/golang/go/src/math/big/arith_arm64.s create mode 100644 vendor/github.com/golang/go/src/math/big/arith_decl.go create mode 100644 vendor/github.com/golang/go/src/math/big/arith_decl_pure.go create mode 100644 vendor/github.com/golang/go/src/math/big/arith_decl_s390x.go create mode 100644 vendor/github.com/golang/go/src/math/big/arith_mips64x.s create mode 100644 vendor/github.com/golang/go/src/math/big/arith_mipsx.s create mode 100644 vendor/github.com/golang/go/src/math/big/arith_ppc64x.s create mode 100644 vendor/github.com/golang/go/src/math/big/arith_s390x.s create mode 100644 vendor/github.com/golang/go/src/math/big/decimal.go create mode 100644 vendor/github.com/golang/go/src/math/big/doc.go create mode 100644 vendor/github.com/golang/go/src/math/big/float.go create mode 100644 vendor/github.com/golang/go/src/math/big/floatconv.go create mode 100644 vendor/github.com/golang/go/src/math/big/floatmarsh.go create mode 100644 vendor/github.com/golang/go/src/math/big/ftoa.go create mode 100644 vendor/github.com/golang/go/src/math/big/int.go create mode 100644 vendor/github.com/golang/go/src/math/big/intconv.go create mode 100644 vendor/github.com/golang/go/src/math/big/intmarsh.go create mode 100644 vendor/github.com/golang/go/src/math/big/nat.go create mode 100644 vendor/github.com/golang/go/src/math/big/natconv.go create mode 100644 vendor/github.com/golang/go/src/math/big/prime.go create mode 100644 vendor/github.com/golang/go/src/math/big/rat.go create mode 100644 vendor/github.com/golang/go/src/math/big/ratconv.go create mode 100644 vendor/github.com/golang/go/src/math/big/ratmarsh.go create mode 100644 vendor/github.com/golang/go/src/math/big/roundingmode_string.go create mode 100644 vendor/github.com/golang/go/src/math/big/sqrt.go diff --git a/helper/keysutil/encrypted_key_storage.go b/helper/keysutil/encrypted_key_storage.go new file mode 100644 index 000000000000..00f961cea6a2 --- /dev/null +++ b/helper/keysutil/encrypted_key_storage.go @@ -0,0 +1,275 @@ +package keysutil + +import ( + "context" + "encoding/base64" + "errors" + paths "path" + "strings" + + "github.com/golang/go/src/math/big" + + "github.com/hashicorp/golang-lru" + "github.com/hashicorp/vault/logical" +) + +const ( + // DefaultCacheSize is used if no cache size is specified for + // NewEncryptedKeyStorage. This value is the number of cache entries to + // store, not the size in bytes of the cache. + DefaultCacheSize = 16 * 1024 + + // DefaultPrefix is used if no prefix is specified for + // NewEncryptedKeyStorage. Prefix must be defined so we can provide context + // for the base folder. + DefaultPrefix = "encryptedkeys/" + + // EncryptedKeyPolicyVersionTpl is a template that can be used to minimize + // the amount of data that's stored with the ciphertext. + EncryptedKeyPolicyVersionTpl = "{{version}}:" +) + +var ( + // ErrPolicyDerivedKeys is returned if the provided policy does not use + // derived keys. This is a requirement for this storage implementation. + ErrPolicyDerivedKeys = errors.New("key policy must use derived keys") + + // ErrPolicyConvergentEncryption is returned if the provided policy does not use + // convergent encryption. This is a requirement for this storage implementation. + ErrPolicyConvergentEncryption = errors.New("key policy must use convergent encryption") + + // ErrPolicyConvergentVersion is returned if the provided policy does not use + // a new enough convergent version. This is a requirement for this storage + // implementation. + ErrPolicyConvergentVersion = errors.New("key policy must use convergent version > 2") + + // ErrNilStorage is returned if the provided storage is nil. + ErrNilStorage = errors.New("nil storage provided") + + // ErrNilPolicy is returned if the provided policy is nil. + ErrNilPolicy = errors.New("nil policy provided") +) + +// EncryptedKeyStorageConfig is used to configure an EncryptedKeyStorage object. +type EncryptedKeyStorageConfig struct { + // Storage is the underlying storage to wrap requests to. + Storage logical.Storage + + // Policy is the key policy to use to encrypt the key paths. + Policy *Policy + + // Prefix is the storage prefix for this instance of the EncryptedKeyStorage + // object. This is stored in plaintext. If not set the DefaultPrefix will be + // used. + Prefix string + + // CacheSize is the number of elements to cache. If not set the + // DetaultCacheSize will be used. + CacheSize int +} + +// NewEncryptedKeyStorage takes an EncryptedKeyStorageConfig and returns a new +// EncryptedKeyStorage object. +func NewEncryptedKeyStorage(config EncryptedKeyStorageConfig) (*EncryptedKeyStorage, error) { + if config.Policy == nil { + return nil, ErrNilPolicy + } + + if !config.Policy.Derived { + return nil, ErrPolicyDerivedKeys + } + + if !config.Policy.ConvergentEncryption { + return nil, ErrPolicyConvergentEncryption + } + + if config.Policy.ConvergentVersion < 2 { + return nil, ErrPolicyConvergentVersion + } + + if config.Storage == nil { + return nil, ErrNilStorage + } + + if config.Prefix == "" { + config.Prefix = DefaultPrefix + } + + if !strings.HasSuffix(config.Prefix, "/") { + config.Prefix += "/" + } + + size := config.CacheSize + if size <= 0 { + size = DefaultCacheSize + } + + cache, err := lru.New2Q(size) + if err != nil { + return nil, err + } + + return &EncryptedKeyStorage{ + policy: config.Policy, + s: config.Storage, + prefix: config.Prefix, + lru: cache, + }, nil +} + +// EncryptedKeyStorage implements the logical.Storage interface and ensures the +// storage paths are encrypted in the underlying storage. +type EncryptedKeyStorage struct { + policy *Policy + s logical.Storage + lru *lru.TwoQueueCache + + prefix string +} + +// List implements the logical.Storage List method, and decrypts all the items +// in a path prefix. This can only operate on full folder structures so the +// prefix should end in a "/". +func (s *EncryptedKeyStorage) List(ctx context.Context, prefix string) ([]string, error) { + encPrefix, err := s.encryptPath(prefix) + if err != nil { + return nil, err + } + + keys, err := s.s.List(ctx, encPrefix+"/") + if err != nil { + return keys, err + } + + decryptedKeys := make([]string, len(keys)) + + // The context for the decryption operations will be the object's prefix + // joined with the provided prefix. Join cleans the path ensuring there + // isn't a trailing "/". + context := []byte(paths.Join(s.prefix, prefix)) + + for i, k := range keys { + raw, ok := s.lru.Get(k) + if ok { + // cache HIT, we can bail early and skip the decode & decrypt operations. + decryptedKeys[i] = raw.(string) + continue + } + + // If a folder is included in the keys it will have a trailing "/". + // We need to remove this before decoding/decrypting and add it back + // later. + appendSlash := strings.HasSuffix(k, "/") + if appendSlash { + k = strings.TrimSuffix(k, "/") + } + + decoded := Base62Decode(k) + if len(decoded) == 0 { + return nil, errors.New("Could not decode key") + } + + // Decrypt the data with the object's key policy. + encodedPlaintext, err := s.policy.Decrypt(context, nil, string(decoded[:])) + if err != nil { + return nil, err + } + + // The plaintext is still base64 encoded, decode it. + decoded, err = base64.StdEncoding.DecodeString(encodedPlaintext) + if err != nil { + return nil, err + } + + plaintext := string(decoded[:]) + + // Add the slash back to the plaintext value + if appendSlash { + plaintext += "/" + k += "/" + } + + // We want to store the unencoded version of the key in the cache. + // This will make it more performent when it's a HIT. + s.lru.Add(k, plaintext) + + decryptedKeys[i] = plaintext + } + + return decryptedKeys, nil +} + +// Get implements the logical.Storage Get method. +func (s *EncryptedKeyStorage) Get(ctx context.Context, path string) (*logical.StorageEntry, error) { + encPath, err := s.encryptPath(path) + if err != nil { + return nil, err + } + + return s.s.Get(ctx, encPath) +} + +// Put implements the logical.Storage Put method. +func (s *EncryptedKeyStorage) Put(ctx context.Context, entry *logical.StorageEntry) error { + encPath, err := s.encryptPath(entry.Key) + if err != nil { + return err + } + e := &logical.StorageEntry{} + *e = *entry + + e.Key = encPath + + return s.s.Put(ctx, e) +} + +// Delete implements the logical.Storage Delete method. +func (s *EncryptedKeyStorage) Delete(ctx context.Context, path string) error { + encPath, err := s.encryptPath(path) + if err != nil { + return err + } + + return s.s.Delete(ctx, encPath) +} + +// encryptPath takes a plaintext path and encrypts each path section (separated +// by "/") with the object's key policy. The context for each encryption is the +// plaintext path prefix for the key. +func (s *EncryptedKeyStorage) encryptPath(path string) (string, error) { + path = paths.Clean(path) + + // Trim the prefix if it starts with a "/" + path = strings.TrimPrefix(path, "/") + + parts := strings.Split(path, "/") + + encPath := s.prefix + context := s.prefix + for _, p := range parts { + encoded := base64.StdEncoding.EncodeToString([]byte(p)) + ciphertext, err := s.policy.Encrypt(0, []byte(context), nil, encoded) + if err != nil { + return "", err + } + + encPath = paths.Join(encPath, Base62Encode([]byte(ciphertext))) + context = paths.Join(context, p) + } + + return encPath, nil +} + +func Base62Encode(buf []byte) string { + encoder := &big.Int{} + + encoder.SetBytes(buf) + return encoder.Text(62) +} + +func Base62Decode(input string) []byte { + decoder := &big.Int{} + + decoder.SetString(input, 62) + return decoder.Bytes() +} diff --git a/helper/keysutil/encrypted_key_storage_test.go b/helper/keysutil/encrypted_key_storage_test.go new file mode 100644 index 000000000000..f700ff589592 --- /dev/null +++ b/helper/keysutil/encrypted_key_storage_test.go @@ -0,0 +1,358 @@ +package keysutil + +import ( + "context" + "fmt" + "reflect" + "sync" + "testing" + + "github.com/hashicorp/vault/logical" +) + +var compilerOpt []string + +func TestBase58(t *testing.T) { + tCases := []struct { + in string + out string + }{ + { + "", + "0", + }, + { + "foo", + "sapp", + }, + { + "5d5746d044b9a9429249966c9e3fee178ca679b91487b11d4b73c9865202104c", + "cozMP2pOYdDiNGeFQ2afKAOGIzO0HVpJ8OPFXuVPNbHasFyenK9CzIIPuOG7EFWOCy4YWvKGZa671N4kRSoaxZ", + }, + { + "5ba33e16d742f3c785f6e7e8bb6f5fe82346ffa1c47aa8e95da4ddd5a55bb334", + "cotpEJPnhuTRofLi4lDe5iKw2fkSGc6TpUYeuWoBp8eLYJBWLRUVDZI414OjOCWXKZ0AI8gqNMoxd4eLOklwYk", + }, + { + " ", + "w", + }, + { + "-", + "J", + }, + { + "0", + "M", + }, + { + "1", + "N", + }, + { + "-1", + "30B", + }, + { + "11", + "3h7", + }, + { + "abc", + "qMin", + }, + { + "1234598760", + "1a0AFzKIPnihTq", + }, + { + "abcdefghijklmnopqrstuvwxyz", + "hUBXsgd3F2swSlEgbVi2p0Ncr6kzVeJTLaW", + }, + } + + for _, c := range tCases { + e := Base62Encode([]byte(c.in)) + d := string(Base62Decode(e)) + + if d != c.in { + t.Fatalf("decoded value didn't match input %#v %#v", c.in, d) + } + + if e != c.out { + t.Fatalf("encoded value didn't match expected %#v, %#v", e, c.out) + } + } + + d := Base62Decode("!0000/") + if len(d) != 0 { + t.Fatalf("Decode of invalid string should be empty, got %#v", d) + } +} + +func TestEncrytedKeysStorage_BadPolicy(t *testing.T) { + s := &logical.InmemStorage{} + policy := &Policy{ + Name: "metadata", + Type: KeyType_AES256_GCM96, + Derived: false, + KDF: Kdf_hkdf_sha256, + ConvergentEncryption: true, + ConvergentVersion: 2, + VersionTemplate: EncryptedKeyPolicyVersionTpl, + versionPrefixCache: &sync.Map{}, + } + + _, err := NewEncryptedKeyStorage(EncryptedKeyStorageConfig{ + Storage: s, + Policy: policy, + Prefix: "prefix", + }) + if err != ErrPolicyDerivedKeys { + t.Fatalf("Unexpected Error: %s", err) + } + + policy = &Policy{ + Name: "metadata", + Type: KeyType_AES256_GCM96, + Derived: true, + KDF: Kdf_hkdf_sha256, + ConvergentEncryption: false, + ConvergentVersion: 2, + VersionTemplate: EncryptedKeyPolicyVersionTpl, + versionPrefixCache: &sync.Map{}, + } + + _, err = NewEncryptedKeyStorage(EncryptedKeyStorageConfig{ + Storage: s, + Policy: policy, + Prefix: "prefix", + }) + if err != ErrPolicyConvergentEncryption { + t.Fatalf("Unexpected Error: %s", err) + } + + policy = &Policy{ + Name: "metadata", + Type: KeyType_AES256_GCM96, + Derived: true, + KDF: Kdf_hkdf_sha256, + ConvergentEncryption: true, + ConvergentVersion: 1, + VersionTemplate: EncryptedKeyPolicyVersionTpl, + versionPrefixCache: &sync.Map{}, + } + + _, err = NewEncryptedKeyStorage(EncryptedKeyStorageConfig{ + Storage: s, + Policy: policy, + Prefix: "prefix", + }) + if err != ErrPolicyConvergentVersion { + t.Fatalf("Unexpected Error: %s", err) + } + + policy = &Policy{ + Name: "metadata", + Type: KeyType_AES256_GCM96, + Derived: true, + KDF: Kdf_hkdf_sha256, + ConvergentEncryption: true, + ConvergentVersion: 2, + VersionTemplate: EncryptedKeyPolicyVersionTpl, + versionPrefixCache: &sync.Map{}, + } + + _, err = NewEncryptedKeyStorage(EncryptedKeyStorageConfig{ + Storage: nil, + Policy: policy, + Prefix: "prefix", + }) + if err != ErrNilStorage { + t.Fatalf("Unexpected Error: %s", err) + } +} + +func TestEncrytedKeysStorage_CRUD(t *testing.T) { + s := &logical.InmemStorage{} + policy := &Policy{ + Name: "metadata", + Type: KeyType_AES256_GCM96, + Derived: true, + KDF: Kdf_hkdf_sha256, + ConvergentEncryption: true, + ConvergentVersion: 2, + VersionTemplate: EncryptedKeyPolicyVersionTpl, + versionPrefixCache: &sync.Map{}, + } + + ctx := context.Background() + + err := policy.Rotate(ctx, s) + if err != nil { + t.Fatal(err) + } + + es, err := NewEncryptedKeyStorage(EncryptedKeyStorageConfig{ + Storage: s, + Policy: policy, + Prefix: "prefix", + }) + if err != nil { + t.Fatal(err) + } + + err = es.Put(ctx, &logical.StorageEntry{ + Key: "test/foo", + Value: []byte("test"), + }) + if err != nil { + t.Fatal(err) + } + + err = es.Put(ctx, &logical.StorageEntry{ + Key: "test/foo1/test", + Value: []byte("test"), + }) + if err != nil { + t.Fatal(err) + } + + keys, err := es.List(ctx, "test/") + if err != nil { + t.Fatal(err) + } + + // Test prefixed with "/" + keys, err = es.List(ctx, "/test/") + if err != nil { + t.Fatal(err) + } + + if len(keys) != 2 || keys[0] != "foo1/" || keys[1] != "foo" { + t.Fatalf("bad keys: %#v", keys) + } + + // Test the cached value is correct + keys, err = es.List(ctx, "test/") + if err != nil { + t.Fatal(err) + } + + if len(keys) != 2 || keys[0] != "foo1/" || keys[1] != "foo" { + t.Fatalf("bad keys: %#v", keys) + } + + data, err := es.Get(ctx, "test/foo") + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(data.Value, []byte("test")) { + t.Fatalf("bad data: %#v", data) + } + + err = es.Delete(ctx, "test/foo") + if err != nil { + t.Fatal(err) + } + + data, err = es.Get(ctx, "test/foo") + if err != nil { + t.Fatal(err) + } + if data != nil { + t.Fatal("data should be nil") + } + +} + +func BenchmarkEncrytedKeyStorage_List(b *testing.B) { + s := &logical.InmemStorage{} + policy := &Policy{ + Name: "metadata", + Type: KeyType_AES256_GCM96, + Derived: true, + KDF: Kdf_hkdf_sha256, + ConvergentEncryption: true, + ConvergentVersion: 2, + VersionTemplate: EncryptedKeyPolicyVersionTpl, + versionPrefixCache: &sync.Map{}, + } + + ctx := context.Background() + + err := policy.Rotate(ctx, s) + if err != nil { + b.Fatal(err) + } + + es, err := NewEncryptedKeyStorage(EncryptedKeyStorageConfig{ + Storage: s, + Policy: policy, + Prefix: "prefix", + }) + if err != nil { + b.Fatal(err) + } + + for i := 0; i < 10000; i++ { + err = es.Put(ctx, &logical.StorageEntry{ + Key: fmt.Sprintf("test/%d", i), + Value: []byte("test"), + }) + if err != nil { + b.Fatal(err) + } + } + b.ResetTimer() + + for i := 0; i < b.N; i++ { + keys, err := es.List(ctx, "test/") + if err != nil { + b.Fatal(err) + } + compilerOpt = keys + } +} + +func BenchmarkEncrytedKeyStorage_Put(b *testing.B) { + s := &logical.InmemStorage{} + policy := &Policy{ + Name: "metadata", + Type: KeyType_AES256_GCM96, + Derived: true, + KDF: Kdf_hkdf_sha256, + ConvergentEncryption: true, + ConvergentVersion: 2, + VersionTemplate: EncryptedKeyPolicyVersionTpl, + versionPrefixCache: &sync.Map{}, + } + + ctx := context.Background() + + err := policy.Rotate(ctx, s) + if err != nil { + b.Fatal(err) + } + + es, err := NewEncryptedKeyStorage(EncryptedKeyStorageConfig{ + Storage: s, + Policy: policy, + Prefix: "prefix", + }) + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + err = es.Put(ctx, &logical.StorageEntry{ + Key: fmt.Sprintf("test/%d", i), + Value: []byte("test"), + }) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/helper/keysutil/lock_manager.go b/helper/keysutil/lock_manager.go index f1c505a7b49c..24b25f380c01 100644 --- a/helper/keysutil/lock_manager.go +++ b/helper/keysutil/lock_manager.go @@ -239,6 +239,9 @@ func (lm *LockManager) RestorePolicy(ctx context.Context, storage logical.Storag name = keyData.Policy.Name + // set the policy version cache + keyData.Policy.versionPrefixCache = &sync.Map{} + lockType := exclusive lock := lm.policyLock(name, lockType) defer lm.UnlockPolicy(lock, lockType) @@ -387,6 +390,7 @@ func (lm *LockManager) getPolicyCommon(ctx context.Context, req PolicyRequest, l Derived: req.Derived, Exportable: req.Exportable, AllowPlaintextBackup: req.AllowPlaintextBackup, + versionPrefixCache: &sync.Map{}, } if req.Derived { p.KDF = Kdf_hkdf_sha256 @@ -512,5 +516,7 @@ func (lm *LockManager) getStoredPolicy(ctx context.Context, storage logical.Stor return nil, err } + policy.versionPrefixCache = &sync.Map{} + return &policy, nil } diff --git a/helper/keysutil/policy.go b/helper/keysutil/policy.go index e5770904a072..503d11079fc5 100644 --- a/helper/keysutil/policy.go +++ b/helper/keysutil/policy.go @@ -20,8 +20,10 @@ import ( "fmt" "io" "math/big" + "path" "strconv" "strings" + "sync" "time" "golang.org/x/crypto/chacha20poly1305" @@ -52,7 +54,14 @@ const ( KeyType_ChaCha20_Poly1305 ) -const ErrTooOld = "ciphertext or signature version is disallowed by policy (too old)" +const ( + // ErrTooOld is returned whtn the ciphertext or signatures's key version is + // too old. + ErrTooOld = "ciphertext or signature version is disallowed by policy (too old)" + + // DefaultVersionTemplate is used when no version template is provided. + DefaultVersionTemplate = "vault:v{{version}}:" +) type RestoreInfo struct { Time time.Time `json:"time"` @@ -244,6 +253,19 @@ type Policy struct { // AllowPlaintextBackup allows taking backup of the policy in plaintext AllowPlaintextBackup bool `json:"allow_plaintext_backup"` + + // VersionTemplate is used to prefix the ciphertext with information about + // the key version. It must inclide {{version}} and a delimiter between the + // version prefix and the ciphertext. + VersionTemplate string `json:"version_template"` + + // StoragePrefix is used to add a prefix when storing and retrieving the + // policy object. + StoragePrefix string + + // versionPrefixCache stores caches of verison prefix strings and the split + // version template. + versionPrefixCache *sync.Map } // ArchivedKeys stores old keys. This is used to keep the key loading time sane @@ -255,7 +277,7 @@ type archivedKeys struct { func (p *Policy) LoadArchive(ctx context.Context, storage logical.Storage) (*archivedKeys, error) { archive := &archivedKeys{} - raw, err := storage.Get(ctx, "archive/"+p.Name) + raw, err := storage.Get(ctx, path.Join(p.StoragePrefix, "archive", p.Name)) if err != nil { return nil, err } @@ -280,7 +302,7 @@ func (p *Policy) storeArchive(ctx context.Context, storage logical.Storage, arch // Write the policy into storage err = storage.Put(ctx, &logical.StorageEntry{ - Key: "archive/" + p.Name, + Key: path.Join(p.StoragePrefix, "archive", p.Name), Value: buf, }) if err != nil { @@ -405,7 +427,7 @@ func (p *Policy) Persist(ctx context.Context, storage logical.Storage) (retErr e // Write the policy into storage err = storage.Put(ctx, &logical.StorageEntry{ - Key: "policy/" + p.Name, + Key: path.Join(p.StoragePrefix, "policy", p.Name), Value: buf, }) if err != nil { @@ -703,7 +725,7 @@ func (p *Policy) Encrypt(ver int, context, nonce []byte, value string) (string, encoded := base64.StdEncoding.EncodeToString(ciphertext) // Prepend some information - encoded = "vault:v" + strconv.Itoa(ver) + ":" + encoded + encoded = p.getVersionPrefix(ver) + encoded return encoded, nil } @@ -713,8 +735,13 @@ func (p *Policy) Decrypt(context, nonce []byte, value string) (string, error) { return "", errutil.UserError{Err: fmt.Sprintf("message decryption not supported for key type %v", p.Type)} } + tplParts, err := p.getTemplateParts() + if err != nil { + return "", err + } + // Verify the prefix - if !strings.HasPrefix(value, "vault:v") { + if !strings.HasPrefix(value, tplParts[0]) { return "", errutil.UserError{Err: "invalid ciphertext: no prefix"} } @@ -722,7 +749,7 @@ func (p *Policy) Decrypt(context, nonce []byte, value string) (string, error) { return "", errutil.UserError{Err: "invalid convergent nonce supplied"} } - splitVerCiphertext := strings.SplitN(strings.TrimPrefix(value, "vault:v"), ":", 2) + splitVerCiphertext := strings.SplitN(strings.TrimPrefix(value, tplParts[0]), tplParts[1], 2) if len(splitVerCiphertext) != 2 { return "", errutil.UserError{Err: "invalid ciphertext: wrong number of fields"} } @@ -929,9 +956,8 @@ func (p *Policy) Sign(ver int, context, input []byte, algorithm string) (*Signin // Convert to base64 encoded := base64.StdEncoding.EncodeToString(sig) - res := &SigningResult{ - Signature: "vault:v" + strconv.Itoa(ver) + ":" + encoded, + Signature: p.getVersionPrefix(ver) + encoded, PublicKey: pubKey, } @@ -943,12 +969,17 @@ func (p *Policy) VerifySignature(context, input []byte, sig, algorithm string) ( return false, errutil.UserError{Err: fmt.Sprintf("message verification not supported for key type %v", p.Type)} } + tplParts, err := p.getTemplateParts() + if err != nil { + return false, err + } + // Verify the prefix - if !strings.HasPrefix(sig, "vault:v") { + if !strings.HasPrefix(sig, tplParts[0]) { return false, errutil.UserError{Err: "invalid signature: no prefix"} } - splitVerSig := strings.SplitN(strings.TrimPrefix(sig, "vault:v"), ":", 2) + splitVerSig := strings.SplitN(strings.TrimPrefix(sig, tplParts[0]), tplParts[1], 2) if len(splitVerSig) != 2 { return false, errutil.UserError{Err: "invalid signature: wrong number of fields"} } @@ -1195,3 +1226,40 @@ func (p *Policy) Backup(ctx context.Context, storage logical.Storage) (out strin return base64.StdEncoding.EncodeToString(encodedBackup), nil } + +func (p *Policy) getTemplateParts() ([]string, error) { + partsRaw, ok := p.versionPrefixCache.Load("template-parts") + if ok { + return partsRaw.([]string), nil + } + + template := p.VersionTemplate + if template == "" { + template = DefaultVersionTemplate + } + + tplParts := strings.Split(template, "{{version}}") + if len(tplParts) != 2 { + return nil, errutil.InternalError{Err: "error parsing version template"} + } + + p.versionPrefixCache.Store("template-parts", tplParts) + return tplParts, nil +} + +func (p *Policy) getVersionPrefix(ver int) string { + prefixRaw, ok := p.versionPrefixCache.Load(ver) + if ok { + return prefixRaw.(string) + } + + template := p.VersionTemplate + if template == "" { + template = DefaultVersionTemplate + } + + prefix := strings.Replace(template, "{{version}}", strconv.Itoa(ver), -1) + p.versionPrefixCache.Store(ver, prefix) + + return prefix +} diff --git a/helper/keysutil/policy_test.go b/helper/keysutil/policy_test.go index d1e745fea17f..3f7f20917abe 100644 --- a/helper/keysutil/policy_test.go +++ b/helper/keysutil/policy_test.go @@ -471,6 +471,7 @@ func Test_BadUpgrade(t *testing.T) { k.CreationTime = o.CreationTime k.HMACKey = o.HMACKey p.Keys["1"] = k + p.versionPrefixCache = nil if !reflect.DeepEqual(orig, p) { t.Fatalf("not equal:\n%#v\n%#v", orig, p) diff --git a/vendor/github.com/golang/go/LICENSE b/vendor/github.com/golang/go/LICENSE new file mode 100644 index 000000000000..6a66aea5eafe --- /dev/null +++ b/vendor/github.com/golang/go/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/golang/go/PATENTS b/vendor/github.com/golang/go/PATENTS new file mode 100644 index 000000000000..733099041f84 --- /dev/null +++ b/vendor/github.com/golang/go/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/github.com/golang/go/src/math/big/accuracy_string.go b/vendor/github.com/golang/go/src/math/big/accuracy_string.go new file mode 100644 index 000000000000..24ef7f107700 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/accuracy_string.go @@ -0,0 +1,17 @@ +// generated by stringer -type=Accuracy; DO NOT EDIT + +package big + +import "fmt" + +const _Accuracy_name = "BelowExactAbove" + +var _Accuracy_index = [...]uint8{0, 5, 10, 15} + +func (i Accuracy) String() string { + i -= -1 + if i < 0 || i+1 >= Accuracy(len(_Accuracy_index)) { + return fmt.Sprintf("Accuracy(%d)", i+-1) + } + return _Accuracy_name[_Accuracy_index[i]:_Accuracy_index[i+1]] +} diff --git a/vendor/github.com/golang/go/src/math/big/arith.go b/vendor/github.com/golang/go/src/math/big/arith.go new file mode 100644 index 000000000000..ad352403a7c5 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/arith.go @@ -0,0 +1,260 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file provides Go implementations of elementary multi-precision +// arithmetic operations on word vectors. Needed for platforms without +// assembly implementations of these routines. + +package big + +import "math/bits" + +// A Word represents a single digit of a multi-precision unsigned integer. +type Word uint + +const ( + _S = _W / 8 // word size in bytes + + _W = bits.UintSize // word size in bits + _B = 1 << _W // digit base + _M = _B - 1 // digit mask + + _W2 = _W / 2 // half word size in bits + _B2 = 1 << _W2 // half digit base + _M2 = _B2 - 1 // half digit mask +) + +// ---------------------------------------------------------------------------- +// Elementary operations on words +// +// These operations are used by the vector operations below. + +// z1<<_W + z0 = x+y+c, with c == 0 or 1 +func addWW_g(x, y, c Word) (z1, z0 Word) { + yc := y + c + z0 = x + yc + if z0 < x || yc < y { + z1 = 1 + } + return +} + +// z1<<_W + z0 = x-y-c, with c == 0 or 1 +func subWW_g(x, y, c Word) (z1, z0 Word) { + yc := y + c + z0 = x - yc + if z0 > x || yc < y { + z1 = 1 + } + return +} + +// z1<<_W + z0 = x*y +// Adapted from Warren, Hacker's Delight, p. 132. +func mulWW_g(x, y Word) (z1, z0 Word) { + x0 := x & _M2 + x1 := x >> _W2 + y0 := y & _M2 + y1 := y >> _W2 + w0 := x0 * y0 + t := x1*y0 + w0>>_W2 + w1 := t & _M2 + w2 := t >> _W2 + w1 += x0 * y1 + z1 = x1*y1 + w2 + w1>>_W2 + z0 = x * y + return +} + +// z1<<_W + z0 = x*y + c +func mulAddWWW_g(x, y, c Word) (z1, z0 Word) { + z1, zz0 := mulWW_g(x, y) + if z0 = zz0 + c; z0 < zz0 { + z1++ + } + return +} + +// nlz returns the number of leading zeros in x. +// Wraps bits.LeadingZeros call for convenience. +func nlz(x Word) uint { + return uint(bits.LeadingZeros(uint(x))) +} + +// q = (u1<<_W + u0 - r)/y +// Adapted from Warren, Hacker's Delight, p. 152. +func divWW_g(u1, u0, v Word) (q, r Word) { + if u1 >= v { + return 1<<_W - 1, 1<<_W - 1 + } + + s := nlz(v) + v <<= s + + vn1 := v >> _W2 + vn0 := v & _M2 + un32 := u1<>(_W-s) + un10 := u0 << s + un1 := un10 >> _W2 + un0 := un10 & _M2 + q1 := un32 / vn1 + rhat := un32 - q1*vn1 + + for q1 >= _B2 || q1*vn0 > _B2*rhat+un1 { + q1-- + rhat += vn1 + if rhat >= _B2 { + break + } + } + + un21 := un32*_B2 + un1 - q1*v + q0 := un21 / vn1 + rhat = un21 - q0*vn1 + + for q0 >= _B2 || q0*vn0 > _B2*rhat+un0 { + q0-- + rhat += vn1 + if rhat >= _B2 { + break + } + } + + return q1*_B2 + q0, (un21*_B2 + un0 - q0*v) >> s +} + +// Keep for performance debugging. +// Using addWW_g is likely slower. +const use_addWW_g = false + +// The resulting carry c is either 0 or 1. +func addVV_g(z, x, y []Word) (c Word) { + if use_addWW_g { + for i := range z { + c, z[i] = addWW_g(x[i], y[i], c) + } + return + } + + for i, xi := range x[:len(z)] { + yi := y[i] + zi := xi + yi + c + z[i] = zi + // see "Hacker's Delight", section 2-12 (overflow detection) + c = (xi&yi | (xi|yi)&^zi) >> (_W - 1) + } + return +} + +// The resulting carry c is either 0 or 1. +func subVV_g(z, x, y []Word) (c Word) { + if use_addWW_g { + for i := range z { + c, z[i] = subWW_g(x[i], y[i], c) + } + return + } + + for i, xi := range x[:len(z)] { + yi := y[i] + zi := xi - yi - c + z[i] = zi + // see "Hacker's Delight", section 2-12 (overflow detection) + c = (yi&^xi | (yi|^xi)&zi) >> (_W - 1) + } + return +} + +// The resulting carry c is either 0 or 1. +func addVW_g(z, x []Word, y Word) (c Word) { + if use_addWW_g { + c = y + for i := range z { + c, z[i] = addWW_g(x[i], c, 0) + } + return + } + + c = y + for i, xi := range x[:len(z)] { + zi := xi + c + z[i] = zi + c = xi &^ zi >> (_W - 1) + } + return +} + +func subVW_g(z, x []Word, y Word) (c Word) { + if use_addWW_g { + c = y + for i := range z { + c, z[i] = subWW_g(x[i], c, 0) + } + return + } + + c = y + for i, xi := range x[:len(z)] { + zi := xi - c + z[i] = zi + c = (zi &^ xi) >> (_W - 1) + } + return +} + +func shlVU_g(z, x []Word, s uint) (c Word) { + if n := len(z); n > 0 { + ŝ := _W - s + w1 := x[n-1] + c = w1 >> ŝ + for i := n - 1; i > 0; i-- { + w := w1 + w1 = x[i-1] + z[i] = w<>ŝ + } + z[0] = w1 << s + } + return +} + +func shrVU_g(z, x []Word, s uint) (c Word) { + if n := len(z); n > 0 { + ŝ := _W - s + w1 := x[0] + c = w1 << ŝ + for i := 0; i < n-1; i++ { + w := w1 + w1 = x[i+1] + z[i] = w>>s | w1<<ŝ + } + z[n-1] = w1 >> s + } + return +} + +func mulAddVWW_g(z, x []Word, y, r Word) (c Word) { + c = r + for i := range z { + c, z[i] = mulAddWWW_g(x[i], y, c) + } + return +} + +// TODO(gri) Remove use of addWW_g here and then we can remove addWW_g and subWW_g. +func addMulVVW_g(z, x []Word, y Word) (c Word) { + for i := range z { + z1, z0 := mulAddWWW_g(x[i], y, z[i]) + c, z[i] = addWW_g(z0, c, 0) + c += z1 + } + return +} + +func divWVW_g(z []Word, xn Word, x []Word, y Word) (r Word) { + r = xn + for i := len(z) - 1; i >= 0; i-- { + z[i], r = divWW_g(r, x[i], y) + } + return +} diff --git a/vendor/github.com/golang/go/src/math/big/arith_386.s b/vendor/github.com/golang/go/src/math/big/arith_386.s new file mode 100644 index 000000000000..6c080f074a3c --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/arith_386.s @@ -0,0 +1,271 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !math_big_pure_go + +#include "textflag.h" + +// This file provides fast assembly versions for the elementary +// arithmetic operations on vectors implemented in arith.go. + +// func mulWW(x, y Word) (z1, z0 Word) +TEXT ·mulWW(SB),NOSPLIT,$0 + MOVL x+0(FP), AX + MULL y+4(FP) + MOVL DX, z1+8(FP) + MOVL AX, z0+12(FP) + RET + + +// func divWW(x1, x0, y Word) (q, r Word) +TEXT ·divWW(SB),NOSPLIT,$0 + MOVL x1+0(FP), DX + MOVL x0+4(FP), AX + DIVL y+8(FP) + MOVL AX, q+12(FP) + MOVL DX, r+16(FP) + RET + + +// func addVV(z, x, y []Word) (c Word) +TEXT ·addVV(SB),NOSPLIT,$0 + MOVL z+0(FP), DI + MOVL x+12(FP), SI + MOVL y+24(FP), CX + MOVL z_len+4(FP), BP + MOVL $0, BX // i = 0 + MOVL $0, DX // c = 0 + JMP E1 + +L1: MOVL (SI)(BX*4), AX + ADDL DX, DX // restore CF + ADCL (CX)(BX*4), AX + SBBL DX, DX // save CF + MOVL AX, (DI)(BX*4) + ADDL $1, BX // i++ + +E1: CMPL BX, BP // i < n + JL L1 + + NEGL DX + MOVL DX, c+36(FP) + RET + + +// func subVV(z, x, y []Word) (c Word) +// (same as addVV except for SBBL instead of ADCL and label names) +TEXT ·subVV(SB),NOSPLIT,$0 + MOVL z+0(FP), DI + MOVL x+12(FP), SI + MOVL y+24(FP), CX + MOVL z_len+4(FP), BP + MOVL $0, BX // i = 0 + MOVL $0, DX // c = 0 + JMP E2 + +L2: MOVL (SI)(BX*4), AX + ADDL DX, DX // restore CF + SBBL (CX)(BX*4), AX + SBBL DX, DX // save CF + MOVL AX, (DI)(BX*4) + ADDL $1, BX // i++ + +E2: CMPL BX, BP // i < n + JL L2 + + NEGL DX + MOVL DX, c+36(FP) + RET + + +// func addVW(z, x []Word, y Word) (c Word) +TEXT ·addVW(SB),NOSPLIT,$0 + MOVL z+0(FP), DI + MOVL x+12(FP), SI + MOVL y+24(FP), AX // c = y + MOVL z_len+4(FP), BP + MOVL $0, BX // i = 0 + JMP E3 + +L3: ADDL (SI)(BX*4), AX + MOVL AX, (DI)(BX*4) + SBBL AX, AX // save CF + NEGL AX + ADDL $1, BX // i++ + +E3: CMPL BX, BP // i < n + JL L3 + + MOVL AX, c+28(FP) + RET + + +// func subVW(z, x []Word, y Word) (c Word) +TEXT ·subVW(SB),NOSPLIT,$0 + MOVL z+0(FP), DI + MOVL x+12(FP), SI + MOVL y+24(FP), AX // c = y + MOVL z_len+4(FP), BP + MOVL $0, BX // i = 0 + JMP E4 + +L4: MOVL (SI)(BX*4), DX + SUBL AX, DX + MOVL DX, (DI)(BX*4) + SBBL AX, AX // save CF + NEGL AX + ADDL $1, BX // i++ + +E4: CMPL BX, BP // i < n + JL L4 + + MOVL AX, c+28(FP) + RET + + +// func shlVU(z, x []Word, s uint) (c Word) +TEXT ·shlVU(SB),NOSPLIT,$0 + MOVL z_len+4(FP), BX // i = z + SUBL $1, BX // i-- + JL X8b // i < 0 (n <= 0) + + // n > 0 + MOVL z+0(FP), DI + MOVL x+12(FP), SI + MOVL s+24(FP), CX + MOVL (SI)(BX*4), AX // w1 = x[n-1] + MOVL $0, DX + SHLL CX, DX:AX // w1>>ŝ + MOVL DX, c+28(FP) + + CMPL BX, $0 + JLE X8a // i <= 0 + + // i > 0 +L8: MOVL AX, DX // w = w1 + MOVL -4(SI)(BX*4), AX // w1 = x[i-1] + SHLL CX, DX:AX // w<>ŝ + MOVL DX, (DI)(BX*4) // z[i] = w<>ŝ + SUBL $1, BX // i-- + JG L8 // i > 0 + + // i <= 0 +X8a: SHLL CX, AX // w1< 0 + MOVL z+0(FP), DI + MOVL x+12(FP), SI + MOVL s+24(FP), CX + MOVL (SI), AX // w1 = x[0] + MOVL $0, DX + SHRL CX, DX:AX // w1<<ŝ + MOVL DX, c+28(FP) + + MOVL $0, BX // i = 0 + JMP E9 + + // i < n-1 +L9: MOVL AX, DX // w = w1 + MOVL 4(SI)(BX*4), AX // w1 = x[i+1] + SHRL CX, DX:AX // w>>s | w1<<ŝ + MOVL DX, (DI)(BX*4) // z[i] = w>>s | w1<<ŝ + ADDL $1, BX // i++ + +E9: CMPL BX, BP + JL L9 // i < n-1 + + // i >= n-1 +X9a: SHRL CX, AX // w1>>s + MOVL AX, (DI)(BP*4) // z[n-1] = w1>>s + RET + +X9b: MOVL $0, c+28(FP) + RET + + +// func mulAddVWW(z, x []Word, y, r Word) (c Word) +TEXT ·mulAddVWW(SB),NOSPLIT,$0 + MOVL z+0(FP), DI + MOVL x+12(FP), SI + MOVL y+24(FP), BP + MOVL r+28(FP), CX // c = r + MOVL z_len+4(FP), BX + LEAL (DI)(BX*4), DI + LEAL (SI)(BX*4), SI + NEGL BX // i = -n + JMP E5 + +L5: MOVL (SI)(BX*4), AX + MULL BP + ADDL CX, AX + ADCL $0, DX + MOVL AX, (DI)(BX*4) + MOVL DX, CX + ADDL $1, BX // i++ + +E5: CMPL BX, $0 // i < 0 + JL L5 + + MOVL CX, c+32(FP) + RET + + +// func addMulVVW(z, x []Word, y Word) (c Word) +TEXT ·addMulVVW(SB),NOSPLIT,$0 + MOVL z+0(FP), DI + MOVL x+12(FP), SI + MOVL y+24(FP), BP + MOVL z_len+4(FP), BX + LEAL (DI)(BX*4), DI + LEAL (SI)(BX*4), SI + NEGL BX // i = -n + MOVL $0, CX // c = 0 + JMP E6 + +L6: MOVL (SI)(BX*4), AX + MULL BP + ADDL CX, AX + ADCL $0, DX + ADDL AX, (DI)(BX*4) + ADCL $0, DX + MOVL DX, CX + ADDL $1, BX // i++ + +E6: CMPL BX, $0 // i < 0 + JL L6 + + MOVL CX, c+28(FP) + RET + + +// func divWVW(z* Word, xn Word, x []Word, y Word) (r Word) +TEXT ·divWVW(SB),NOSPLIT,$0 + MOVL z+0(FP), DI + MOVL xn+12(FP), DX // r = xn + MOVL x+16(FP), SI + MOVL y+28(FP), CX + MOVL z_len+4(FP), BX // i = z + JMP E7 + +L7: MOVL (SI)(BX*4), AX + DIVL CX + MOVL AX, (DI)(BX*4) + +E7: SUBL $1, BX // i-- + JGE L7 // i >= 0 + + MOVL DX, r+32(FP) + RET diff --git a/vendor/github.com/golang/go/src/math/big/arith_amd64.s b/vendor/github.com/golang/go/src/math/big/arith_amd64.s new file mode 100644 index 000000000000..9a2405ee1c24 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/arith_amd64.s @@ -0,0 +1,450 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !math_big_pure_go + +#include "textflag.h" + +// This file provides fast assembly versions for the elementary +// arithmetic operations on vectors implemented in arith.go. + +// func mulWW(x, y Word) (z1, z0 Word) +TEXT ·mulWW(SB),NOSPLIT,$0 + MOVQ x+0(FP), AX + MULQ y+8(FP) + MOVQ DX, z1+16(FP) + MOVQ AX, z0+24(FP) + RET + + +// func divWW(x1, x0, y Word) (q, r Word) +TEXT ·divWW(SB),NOSPLIT,$0 + MOVQ x1+0(FP), DX + MOVQ x0+8(FP), AX + DIVQ y+16(FP) + MOVQ AX, q+24(FP) + MOVQ DX, r+32(FP) + RET + +// The carry bit is saved with SBBQ Rx, Rx: if the carry was set, Rx is -1, otherwise it is 0. +// It is restored with ADDQ Rx, Rx: if Rx was -1 the carry is set, otherwise it is cleared. +// This is faster than using rotate instructions. + +// func addVV(z, x, y []Word) (c Word) +TEXT ·addVV(SB),NOSPLIT,$0 + MOVQ z_len+8(FP), DI + MOVQ x+24(FP), R8 + MOVQ y+48(FP), R9 + MOVQ z+0(FP), R10 + + MOVQ $0, CX // c = 0 + MOVQ $0, SI // i = 0 + + // s/JL/JMP/ below to disable the unrolled loop + SUBQ $4, DI // n -= 4 + JL V1 // if n < 0 goto V1 + +U1: // n >= 0 + // regular loop body unrolled 4x + ADDQ CX, CX // restore CF + MOVQ 0(R8)(SI*8), R11 + MOVQ 8(R8)(SI*8), R12 + MOVQ 16(R8)(SI*8), R13 + MOVQ 24(R8)(SI*8), R14 + ADCQ 0(R9)(SI*8), R11 + ADCQ 8(R9)(SI*8), R12 + ADCQ 16(R9)(SI*8), R13 + ADCQ 24(R9)(SI*8), R14 + MOVQ R11, 0(R10)(SI*8) + MOVQ R12, 8(R10)(SI*8) + MOVQ R13, 16(R10)(SI*8) + MOVQ R14, 24(R10)(SI*8) + SBBQ CX, CX // save CF + + ADDQ $4, SI // i += 4 + SUBQ $4, DI // n -= 4 + JGE U1 // if n >= 0 goto U1 + +V1: ADDQ $4, DI // n += 4 + JLE E1 // if n <= 0 goto E1 + +L1: // n > 0 + ADDQ CX, CX // restore CF + MOVQ 0(R8)(SI*8), R11 + ADCQ 0(R9)(SI*8), R11 + MOVQ R11, 0(R10)(SI*8) + SBBQ CX, CX // save CF + + ADDQ $1, SI // i++ + SUBQ $1, DI // n-- + JG L1 // if n > 0 goto L1 + +E1: NEGQ CX + MOVQ CX, c+72(FP) // return c + RET + + +// func subVV(z, x, y []Word) (c Word) +// (same as addVV except for SBBQ instead of ADCQ and label names) +TEXT ·subVV(SB),NOSPLIT,$0 + MOVQ z_len+8(FP), DI + MOVQ x+24(FP), R8 + MOVQ y+48(FP), R9 + MOVQ z+0(FP), R10 + + MOVQ $0, CX // c = 0 + MOVQ $0, SI // i = 0 + + // s/JL/JMP/ below to disable the unrolled loop + SUBQ $4, DI // n -= 4 + JL V2 // if n < 0 goto V2 + +U2: // n >= 0 + // regular loop body unrolled 4x + ADDQ CX, CX // restore CF + MOVQ 0(R8)(SI*8), R11 + MOVQ 8(R8)(SI*8), R12 + MOVQ 16(R8)(SI*8), R13 + MOVQ 24(R8)(SI*8), R14 + SBBQ 0(R9)(SI*8), R11 + SBBQ 8(R9)(SI*8), R12 + SBBQ 16(R9)(SI*8), R13 + SBBQ 24(R9)(SI*8), R14 + MOVQ R11, 0(R10)(SI*8) + MOVQ R12, 8(R10)(SI*8) + MOVQ R13, 16(R10)(SI*8) + MOVQ R14, 24(R10)(SI*8) + SBBQ CX, CX // save CF + + ADDQ $4, SI // i += 4 + SUBQ $4, DI // n -= 4 + JGE U2 // if n >= 0 goto U2 + +V2: ADDQ $4, DI // n += 4 + JLE E2 // if n <= 0 goto E2 + +L2: // n > 0 + ADDQ CX, CX // restore CF + MOVQ 0(R8)(SI*8), R11 + SBBQ 0(R9)(SI*8), R11 + MOVQ R11, 0(R10)(SI*8) + SBBQ CX, CX // save CF + + ADDQ $1, SI // i++ + SUBQ $1, DI // n-- + JG L2 // if n > 0 goto L2 + +E2: NEGQ CX + MOVQ CX, c+72(FP) // return c + RET + + +// func addVW(z, x []Word, y Word) (c Word) +TEXT ·addVW(SB),NOSPLIT,$0 + MOVQ z_len+8(FP), DI + MOVQ x+24(FP), R8 + MOVQ y+48(FP), CX // c = y + MOVQ z+0(FP), R10 + + MOVQ $0, SI // i = 0 + + // s/JL/JMP/ below to disable the unrolled loop + SUBQ $4, DI // n -= 4 + JL V3 // if n < 4 goto V3 + +U3: // n >= 0 + // regular loop body unrolled 4x + MOVQ 0(R8)(SI*8), R11 + MOVQ 8(R8)(SI*8), R12 + MOVQ 16(R8)(SI*8), R13 + MOVQ 24(R8)(SI*8), R14 + ADDQ CX, R11 + ADCQ $0, R12 + ADCQ $0, R13 + ADCQ $0, R14 + SBBQ CX, CX // save CF + NEGQ CX + MOVQ R11, 0(R10)(SI*8) + MOVQ R12, 8(R10)(SI*8) + MOVQ R13, 16(R10)(SI*8) + MOVQ R14, 24(R10)(SI*8) + + ADDQ $4, SI // i += 4 + SUBQ $4, DI // n -= 4 + JGE U3 // if n >= 0 goto U3 + +V3: ADDQ $4, DI // n += 4 + JLE E3 // if n <= 0 goto E3 + +L3: // n > 0 + ADDQ 0(R8)(SI*8), CX + MOVQ CX, 0(R10)(SI*8) + SBBQ CX, CX // save CF + NEGQ CX + + ADDQ $1, SI // i++ + SUBQ $1, DI // n-- + JG L3 // if n > 0 goto L3 + +E3: MOVQ CX, c+56(FP) // return c + RET + + +// func subVW(z, x []Word, y Word) (c Word) +// (same as addVW except for SUBQ/SBBQ instead of ADDQ/ADCQ and label names) +TEXT ·subVW(SB),NOSPLIT,$0 + MOVQ z_len+8(FP), DI + MOVQ x+24(FP), R8 + MOVQ y+48(FP), CX // c = y + MOVQ z+0(FP), R10 + + MOVQ $0, SI // i = 0 + + // s/JL/JMP/ below to disable the unrolled loop + SUBQ $4, DI // n -= 4 + JL V4 // if n < 4 goto V4 + +U4: // n >= 0 + // regular loop body unrolled 4x + MOVQ 0(R8)(SI*8), R11 + MOVQ 8(R8)(SI*8), R12 + MOVQ 16(R8)(SI*8), R13 + MOVQ 24(R8)(SI*8), R14 + SUBQ CX, R11 + SBBQ $0, R12 + SBBQ $0, R13 + SBBQ $0, R14 + SBBQ CX, CX // save CF + NEGQ CX + MOVQ R11, 0(R10)(SI*8) + MOVQ R12, 8(R10)(SI*8) + MOVQ R13, 16(R10)(SI*8) + MOVQ R14, 24(R10)(SI*8) + + ADDQ $4, SI // i += 4 + SUBQ $4, DI // n -= 4 + JGE U4 // if n >= 0 goto U4 + +V4: ADDQ $4, DI // n += 4 + JLE E4 // if n <= 0 goto E4 + +L4: // n > 0 + MOVQ 0(R8)(SI*8), R11 + SUBQ CX, R11 + MOVQ R11, 0(R10)(SI*8) + SBBQ CX, CX // save CF + NEGQ CX + + ADDQ $1, SI // i++ + SUBQ $1, DI // n-- + JG L4 // if n > 0 goto L4 + +E4: MOVQ CX, c+56(FP) // return c + RET + + +// func shlVU(z, x []Word, s uint) (c Word) +TEXT ·shlVU(SB),NOSPLIT,$0 + MOVQ z_len+8(FP), BX // i = z + SUBQ $1, BX // i-- + JL X8b // i < 0 (n <= 0) + + // n > 0 + MOVQ z+0(FP), R10 + MOVQ x+24(FP), R8 + MOVQ s+48(FP), CX + MOVQ (R8)(BX*8), AX // w1 = x[n-1] + MOVQ $0, DX + SHLQ CX, DX:AX // w1>>ŝ + MOVQ DX, c+56(FP) + + CMPQ BX, $0 + JLE X8a // i <= 0 + + // i > 0 +L8: MOVQ AX, DX // w = w1 + MOVQ -8(R8)(BX*8), AX // w1 = x[i-1] + SHLQ CX, DX:AX // w<>ŝ + MOVQ DX, (R10)(BX*8) // z[i] = w<>ŝ + SUBQ $1, BX // i-- + JG L8 // i > 0 + + // i <= 0 +X8a: SHLQ CX, AX // w1< 0 + MOVQ z+0(FP), R10 + MOVQ x+24(FP), R8 + MOVQ s+48(FP), CX + MOVQ (R8), AX // w1 = x[0] + MOVQ $0, DX + SHRQ CX, DX:AX // w1<<ŝ + MOVQ DX, c+56(FP) + + MOVQ $0, BX // i = 0 + JMP E9 + + // i < n-1 +L9: MOVQ AX, DX // w = w1 + MOVQ 8(R8)(BX*8), AX // w1 = x[i+1] + SHRQ CX, DX:AX // w>>s | w1<<ŝ + MOVQ DX, (R10)(BX*8) // z[i] = w>>s | w1<<ŝ + ADDQ $1, BX // i++ + +E9: CMPQ BX, R11 + JL L9 // i < n-1 + + // i >= n-1 +X9a: SHRQ CX, AX // w1>>s + MOVQ AX, (R10)(R11*8) // z[n-1] = w1>>s + RET + +X9b: MOVQ $0, c+56(FP) + RET + + +// func mulAddVWW(z, x []Word, y, r Word) (c Word) +TEXT ·mulAddVWW(SB),NOSPLIT,$0 + MOVQ z+0(FP), R10 + MOVQ x+24(FP), R8 + MOVQ y+48(FP), R9 + MOVQ r+56(FP), CX // c = r + MOVQ z_len+8(FP), R11 + MOVQ $0, BX // i = 0 + + CMPQ R11, $4 + JL E5 + +U5: // i+4 <= n + // regular loop body unrolled 4x + MOVQ (0*8)(R8)(BX*8), AX + MULQ R9 + ADDQ CX, AX + ADCQ $0, DX + MOVQ AX, (0*8)(R10)(BX*8) + MOVQ DX, CX + MOVQ (1*8)(R8)(BX*8), AX + MULQ R9 + ADDQ CX, AX + ADCQ $0, DX + MOVQ AX, (1*8)(R10)(BX*8) + MOVQ DX, CX + MOVQ (2*8)(R8)(BX*8), AX + MULQ R9 + ADDQ CX, AX + ADCQ $0, DX + MOVQ AX, (2*8)(R10)(BX*8) + MOVQ DX, CX + MOVQ (3*8)(R8)(BX*8), AX + MULQ R9 + ADDQ CX, AX + ADCQ $0, DX + MOVQ AX, (3*8)(R10)(BX*8) + MOVQ DX, CX + ADDQ $4, BX // i += 4 + + LEAQ 4(BX), DX + CMPQ DX, R11 + JLE U5 + JMP E5 + +L5: MOVQ (R8)(BX*8), AX + MULQ R9 + ADDQ CX, AX + ADCQ $0, DX + MOVQ AX, (R10)(BX*8) + MOVQ DX, CX + ADDQ $1, BX // i++ + +E5: CMPQ BX, R11 // i < n + JL L5 + + MOVQ CX, c+64(FP) + RET + + +// func addMulVVW(z, x []Word, y Word) (c Word) +TEXT ·addMulVVW(SB),NOSPLIT,$0 + MOVQ z+0(FP), R10 + MOVQ x+24(FP), R8 + MOVQ y+48(FP), R9 + MOVQ z_len+8(FP), R11 + MOVQ $0, BX // i = 0 + MOVQ $0, CX // c = 0 + MOVQ R11, R12 + ANDQ $-2, R12 + CMPQ R11, $2 + JAE A6 + JMP E6 + +A6: + MOVQ (R8)(BX*8), AX + MULQ R9 + ADDQ (R10)(BX*8), AX + ADCQ $0, DX + ADDQ CX, AX + ADCQ $0, DX + MOVQ DX, CX + MOVQ AX, (R10)(BX*8) + + MOVQ (8)(R8)(BX*8), AX + MULQ R9 + ADDQ (8)(R10)(BX*8), AX + ADCQ $0, DX + ADDQ CX, AX + ADCQ $0, DX + MOVQ DX, CX + MOVQ AX, (8)(R10)(BX*8) + + ADDQ $2, BX + CMPQ BX, R12 + JL A6 + JMP E6 + +L6: MOVQ (R8)(BX*8), AX + MULQ R9 + ADDQ CX, AX + ADCQ $0, DX + ADDQ AX, (R10)(BX*8) + ADCQ $0, DX + MOVQ DX, CX + ADDQ $1, BX // i++ + +E6: CMPQ BX, R11 // i < n + JL L6 + + MOVQ CX, c+56(FP) + RET + + +// func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) +TEXT ·divWVW(SB),NOSPLIT,$0 + MOVQ z+0(FP), R10 + MOVQ xn+24(FP), DX // r = xn + MOVQ x+32(FP), R8 + MOVQ y+56(FP), R9 + MOVQ z_len+8(FP), BX // i = z + JMP E7 + +L7: MOVQ (R8)(BX*8), AX + DIVQ R9 + MOVQ AX, (R10)(BX*8) + +E7: SUBQ $1, BX // i-- + JGE L7 // i >= 0 + + MOVQ DX, r+64(FP) + RET diff --git a/vendor/github.com/golang/go/src/math/big/arith_amd64p32.s b/vendor/github.com/golang/go/src/math/big/arith_amd64p32.s new file mode 100644 index 000000000000..0a672386ccd7 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/arith_amd64p32.s @@ -0,0 +1,40 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !math_big_pure_go + +#include "textflag.h" + +TEXT ·mulWW(SB),NOSPLIT,$0 + JMP ·mulWW_g(SB) + +TEXT ·divWW(SB),NOSPLIT,$0 + JMP ·divWW_g(SB) + +TEXT ·addVV(SB),NOSPLIT,$0 + JMP ·addVV_g(SB) + +TEXT ·subVV(SB),NOSPLIT,$0 + JMP ·subVV_g(SB) + +TEXT ·addVW(SB),NOSPLIT,$0 + JMP ·addVW_g(SB) + +TEXT ·subVW(SB),NOSPLIT,$0 + JMP ·subVW_g(SB) + +TEXT ·shlVU(SB),NOSPLIT,$0 + JMP ·shlVU_g(SB) + +TEXT ·shrVU(SB),NOSPLIT,$0 + JMP ·shrVU_g(SB) + +TEXT ·mulAddVWW(SB),NOSPLIT,$0 + JMP ·mulAddVWW_g(SB) + +TEXT ·addMulVVW(SB),NOSPLIT,$0 + JMP ·addMulVVW_g(SB) + +TEXT ·divWVW(SB),NOSPLIT,$0 + JMP ·divWVW_g(SB) diff --git a/vendor/github.com/golang/go/src/math/big/arith_arm.s b/vendor/github.com/golang/go/src/math/big/arith_arm.s new file mode 100644 index 000000000000..ba65fd2b1fa5 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/arith_arm.s @@ -0,0 +1,294 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !math_big_pure_go + +#include "textflag.h" + +// This file provides fast assembly versions for the elementary +// arithmetic operations on vectors implemented in arith.go. + +// func addVV(z, x, y []Word) (c Word) +TEXT ·addVV(SB),NOSPLIT,$0 + ADD.S $0, R0 // clear carry flag + MOVW z+0(FP), R1 + MOVW z_len+4(FP), R4 + MOVW x+12(FP), R2 + MOVW y+24(FP), R3 + ADD R4<<2, R1, R4 + B E1 +L1: + MOVW.P 4(R2), R5 + MOVW.P 4(R3), R6 + ADC.S R6, R5 + MOVW.P R5, 4(R1) +E1: + TEQ R1, R4 + BNE L1 + + MOVW $0, R0 + MOVW.CS $1, R0 + MOVW R0, c+36(FP) + RET + + +// func subVV(z, x, y []Word) (c Word) +// (same as addVV except for SBC instead of ADC and label names) +TEXT ·subVV(SB),NOSPLIT,$0 + SUB.S $0, R0 // clear borrow flag + MOVW z+0(FP), R1 + MOVW z_len+4(FP), R4 + MOVW x+12(FP), R2 + MOVW y+24(FP), R3 + ADD R4<<2, R1, R4 + B E2 +L2: + MOVW.P 4(R2), R5 + MOVW.P 4(R3), R6 + SBC.S R6, R5 + MOVW.P R5, 4(R1) +E2: + TEQ R1, R4 + BNE L2 + + MOVW $0, R0 + MOVW.CC $1, R0 + MOVW R0, c+36(FP) + RET + + +// func addVW(z, x []Word, y Word) (c Word) +TEXT ·addVW(SB),NOSPLIT,$0 + MOVW z+0(FP), R1 + MOVW z_len+4(FP), R4 + MOVW x+12(FP), R2 + MOVW y+24(FP), R3 + ADD R4<<2, R1, R4 + TEQ R1, R4 + BNE L3a + MOVW R3, c+28(FP) + RET +L3a: + MOVW.P 4(R2), R5 + ADD.S R3, R5 + MOVW.P R5, 4(R1) + B E3 +L3: + MOVW.P 4(R2), R5 + ADC.S $0, R5 + MOVW.P R5, 4(R1) +E3: + TEQ R1, R4 + BNE L3 + + MOVW $0, R0 + MOVW.CS $1, R0 + MOVW R0, c+28(FP) + RET + + +// func subVW(z, x []Word, y Word) (c Word) +TEXT ·subVW(SB),NOSPLIT,$0 + MOVW z+0(FP), R1 + MOVW z_len+4(FP), R4 + MOVW x+12(FP), R2 + MOVW y+24(FP), R3 + ADD R4<<2, R1, R4 + TEQ R1, R4 + BNE L4a + MOVW R3, c+28(FP) + RET +L4a: + MOVW.P 4(R2), R5 + SUB.S R3, R5 + MOVW.P R5, 4(R1) + B E4 +L4: + MOVW.P 4(R2), R5 + SBC.S $0, R5 + MOVW.P R5, 4(R1) +E4: + TEQ R1, R4 + BNE L4 + + MOVW $0, R0 + MOVW.CC $1, R0 + MOVW R0, c+28(FP) + RET + + +// func shlVU(z, x []Word, s uint) (c Word) +TEXT ·shlVU(SB),NOSPLIT,$0 + MOVW z_len+4(FP), R5 + TEQ $0, R5 + BEQ X7 + + MOVW z+0(FP), R1 + MOVW x+12(FP), R2 + ADD R5<<2, R2, R2 + ADD R5<<2, R1, R5 + MOVW s+24(FP), R3 + TEQ $0, R3 // shift 0 is special + BEQ Y7 + ADD $4, R1 // stop one word early + MOVW $32, R4 + SUB R3, R4 + MOVW $0, R7 + + MOVW.W -4(R2), R6 + MOVW R6<>R4, R6 + MOVW R6, c+28(FP) + B E7 + +L7: + MOVW.W -4(R2), R6 + ORR R6>>R4, R7 + MOVW.W R7, -4(R5) + MOVW R6<>R3, R7 + MOVW R6<>R3, R7 +E6: + TEQ R1, R5 + BNE L6 + + MOVW R7, 0(R1) + RET + +Y6: // copy loop, because shift 0 == shift 32 + MOVW.P 4(R2), R6 + MOVW.P R6, 4(R1) + TEQ R1, R5 + BNE Y6 + +X6: + MOVW $0, R1 + MOVW R1, c+28(FP) + RET + + +// func mulAddVWW(z, x []Word, y, r Word) (c Word) +TEXT ·mulAddVWW(SB),NOSPLIT,$0 + MOVW $0, R0 + MOVW z+0(FP), R1 + MOVW z_len+4(FP), R5 + MOVW x+12(FP), R2 + MOVW y+24(FP), R3 + MOVW r+28(FP), R4 + ADD R5<<2, R1, R5 + B E8 + + // word loop +L8: + MOVW.P 4(R2), R6 + MULLU R6, R3, (R7, R6) + ADD.S R4, R6 + ADC R0, R7 + MOVW.P R6, 4(R1) + MOVW R7, R4 +E8: + TEQ R1, R5 + BNE L8 + + MOVW R4, c+32(FP) + RET + + +// func addMulVVW(z, x []Word, y Word) (c Word) +TEXT ·addMulVVW(SB),NOSPLIT,$0 + MOVW $0, R0 + MOVW z+0(FP), R1 + MOVW z_len+4(FP), R5 + MOVW x+12(FP), R2 + MOVW y+24(FP), R3 + ADD R5<<2, R1, R5 + MOVW $0, R4 + B E9 + + // word loop +L9: + MOVW.P 4(R2), R6 + MULLU R6, R3, (R7, R6) + ADD.S R4, R6 + ADC R0, R7 + MOVW 0(R1), R4 + ADD.S R4, R6 + ADC R0, R7 + MOVW.P R6, 4(R1) + MOVW R7, R4 +E9: + TEQ R1, R5 + BNE L9 + + MOVW R4, c+28(FP) + RET + + +// func divWVW(z* Word, xn Word, x []Word, y Word) (r Word) +TEXT ·divWVW(SB),NOSPLIT,$0 + // ARM has no multiword division, so use portable code. + B ·divWVW_g(SB) + + +// func divWW(x1, x0, y Word) (q, r Word) +TEXT ·divWW(SB),NOSPLIT,$0 + // ARM has no multiword division, so use portable code. + B ·divWW_g(SB) + + +// func mulWW(x, y Word) (z1, z0 Word) +TEXT ·mulWW(SB),NOSPLIT,$0 + MOVW x+0(FP), R1 + MOVW y+4(FP), R2 + MULLU R1, R2, (R4, R3) + MOVW R4, z1+8(FP) + MOVW R3, z0+12(FP) + RET diff --git a/vendor/github.com/golang/go/src/math/big/arith_arm64.s b/vendor/github.com/golang/go/src/math/big/arith_arm64.s new file mode 100644 index 000000000000..397b4630a846 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/arith_arm64.s @@ -0,0 +1,167 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !math_big_pure_go + +#include "textflag.h" + +// This file provides fast assembly versions for the elementary +// arithmetic operations on vectors implemented in arith.go. + +// TODO: Consider re-implementing using Advanced SIMD +// once the assembler supports those instructions. + +// func mulWW(x, y Word) (z1, z0 Word) +TEXT ·mulWW(SB),NOSPLIT,$0 + MOVD x+0(FP), R0 + MOVD y+8(FP), R1 + MUL R0, R1, R2 + UMULH R0, R1, R3 + MOVD R3, z1+16(FP) + MOVD R2, z0+24(FP) + RET + + +// func divWW(x1, x0, y Word) (q, r Word) +TEXT ·divWW(SB),NOSPLIT,$0 + B ·divWW_g(SB) // ARM64 has no multiword division + + +// func addVV(z, x, y []Word) (c Word) +TEXT ·addVV(SB),NOSPLIT,$0 + MOVD z+0(FP), R3 + MOVD z_len+8(FP), R0 + MOVD x+24(FP), R1 + MOVD y+48(FP), R2 + ADDS $0, R0 // clear carry flag +loop: + CBZ R0, done // careful not to touch the carry flag + MOVD.P 8(R1), R4 + MOVD.P 8(R2), R5 + ADCS R4, R5 + MOVD.P R5, 8(R3) + SUB $1, R0 + B loop +done: + CSET HS, R0 // extract carry flag + MOVD R0, c+72(FP) + RET + + +// func subVV(z, x, y []Word) (c Word) +TEXT ·subVV(SB),NOSPLIT,$0 + MOVD z+0(FP), R3 + MOVD z_len+8(FP), R0 + MOVD x+24(FP), R1 + MOVD y+48(FP), R2 + CMP R0, R0 // set carry flag +loop: + CBZ R0, done // careful not to touch the carry flag + MOVD.P 8(R1), R4 + MOVD.P 8(R2), R5 + SBCS R5, R4 + MOVD.P R4, 8(R3) + SUB $1, R0 + B loop +done: + CSET LO, R0 // extract carry flag + MOVD R0, c+72(FP) + RET + + +// func addVW(z, x []Word, y Word) (c Word) +TEXT ·addVW(SB),NOSPLIT,$0 + MOVD z+0(FP), R3 + MOVD z_len+8(FP), R0 + MOVD x+24(FP), R1 + MOVD y+48(FP), R2 + CBZ R0, return_y + MOVD.P 8(R1), R4 + ADDS R2, R4 + MOVD.P R4, 8(R3) + SUB $1, R0 +loop: + CBZ R0, done // careful not to touch the carry flag + MOVD.P 8(R1), R4 + ADCS $0, R4 + MOVD.P R4, 8(R3) + SUB $1, R0 + B loop +done: + CSET HS, R0 // extract carry flag + MOVD R0, c+56(FP) + RET +return_y: // z is empty; copy y to c + MOVD R2, c+56(FP) + RET + + +// func subVW(z, x []Word, y Word) (c Word) +TEXT ·subVW(SB),NOSPLIT,$0 + MOVD z+0(FP), R3 + MOVD z_len+8(FP), R0 + MOVD x+24(FP), R1 + MOVD y+48(FP), R2 + CBZ R0, rety + MOVD.P 8(R1), R4 + SUBS R2, R4 + MOVD.P R4, 8(R3) + SUB $1, R0 +loop: + CBZ R0, done // careful not to touch the carry flag + MOVD.P 8(R1), R4 + SBCS $0, R4 + MOVD.P R4, 8(R3) + SUB $1, R0 + B loop +done: + CSET LO, R0 // extract carry flag + MOVD R0, c+56(FP) + RET +rety: // z is empty; copy y to c + MOVD R2, c+56(FP) + RET + + +// func shlVU(z, x []Word, s uint) (c Word) +TEXT ·shlVU(SB),NOSPLIT,$0 + B ·shlVU_g(SB) + + +// func shrVU(z, x []Word, s uint) (c Word) +TEXT ·shrVU(SB),NOSPLIT,$0 + B ·shrVU_g(SB) + + +// func mulAddVWW(z, x []Word, y, r Word) (c Word) +TEXT ·mulAddVWW(SB),NOSPLIT,$0 + MOVD z+0(FP), R1 + MOVD z_len+8(FP), R0 + MOVD x+24(FP), R2 + MOVD y+48(FP), R3 + MOVD r+56(FP), R4 +loop: + CBZ R0, done + MOVD.P 8(R2), R5 + UMULH R5, R3, R7 + MUL R5, R3, R6 + ADDS R4, R6 + ADC $0, R7 + MOVD.P R6, 8(R1) + MOVD R7, R4 + SUB $1, R0 + B loop +done: + MOVD R4, c+64(FP) + RET + + +// func addMulVVW(z, x []Word, y Word) (c Word) +TEXT ·addMulVVW(SB),NOSPLIT,$0 + B ·addMulVVW_g(SB) + + +// func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) +TEXT ·divWVW(SB),NOSPLIT,$0 + B ·divWVW_g(SB) diff --git a/vendor/github.com/golang/go/src/math/big/arith_decl.go b/vendor/github.com/golang/go/src/math/big/arith_decl.go new file mode 100644 index 000000000000..41e592334c37 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/arith_decl.go @@ -0,0 +1,20 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !math_big_pure_go + +package big + +// implemented in arith_$GOARCH.s +func mulWW(x, y Word) (z1, z0 Word) +func divWW(x1, x0, y Word) (q, r Word) +func addVV(z, x, y []Word) (c Word) +func subVV(z, x, y []Word) (c Word) +func addVW(z, x []Word, y Word) (c Word) +func subVW(z, x []Word, y Word) (c Word) +func shlVU(z, x []Word, s uint) (c Word) +func shrVU(z, x []Word, s uint) (c Word) +func mulAddVWW(z, x []Word, y, r Word) (c Word) +func addMulVVW(z, x []Word, y Word) (c Word) +func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) diff --git a/vendor/github.com/golang/go/src/math/big/arith_decl_pure.go b/vendor/github.com/golang/go/src/math/big/arith_decl_pure.go new file mode 100644 index 000000000000..4ae49c123d9f --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/arith_decl_pure.go @@ -0,0 +1,51 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build math_big_pure_go + +package big + +func mulWW(x, y Word) (z1, z0 Word) { + return mulWW_g(x, y) +} + +func divWW(x1, x0, y Word) (q, r Word) { + return divWW_g(x1, x0, y) +} + +func addVV(z, x, y []Word) (c Word) { + return addVV_g(z, x, y) +} + +func subVV(z, x, y []Word) (c Word) { + return subVV_g(z, x, y) +} + +func addVW(z, x []Word, y Word) (c Word) { + return addVW_g(z, x, y) +} + +func subVW(z, x []Word, y Word) (c Word) { + return subVW_g(z, x, y) +} + +func shlVU(z, x []Word, s uint) (c Word) { + return shlVU_g(z, x, s) +} + +func shrVU(z, x []Word, s uint) (c Word) { + return shrVU_g(z, x, s) +} + +func mulAddVWW(z, x []Word, y, r Word) (c Word) { + return mulAddVWW_g(z, x, y, r) +} + +func addMulVVW(z, x []Word, y Word) (c Word) { + return addMulVVW_g(z, x, y) +} + +func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) { + return divWVW_g(z, xn, x, y) +} diff --git a/vendor/github.com/golang/go/src/math/big/arith_decl_s390x.go b/vendor/github.com/golang/go/src/math/big/arith_decl_s390x.go new file mode 100644 index 000000000000..0f11481f6d2e --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/arith_decl_s390x.go @@ -0,0 +1,23 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !math_big_pure_go + +package big + +func addVV_check(z, x, y []Word) (c Word) +func addVV_vec(z, x, y []Word) (c Word) +func addVV_novec(z, x, y []Word) (c Word) +func subVV_check(z, x, y []Word) (c Word) +func subVV_vec(z, x, y []Word) (c Word) +func subVV_novec(z, x, y []Word) (c Word) +func addVW_check(z, x []Word, y Word) (c Word) +func addVW_vec(z, x []Word, y Word) (c Word) +func addVW_novec(z, x []Word, y Word) (c Word) +func subVW_check(z, x []Word, y Word) (c Word) +func subVW_vec(z, x []Word, y Word) (c Word) +func subVW_novec(z, x []Word, y Word) (c Word) +func hasVectorFacility() bool + +var hasVX = hasVectorFacility() diff --git a/vendor/github.com/golang/go/src/math/big/arith_mips64x.s b/vendor/github.com/golang/go/src/math/big/arith_mips64x.s new file mode 100644 index 000000000000..983510ee3d42 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/arith_mips64x.s @@ -0,0 +1,43 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !math_big_pure_go,mips64 !math_big_pure_go,mips64le + +#include "textflag.h" + +// This file provides fast assembly versions for the elementary +// arithmetic operations on vectors implemented in arith.go. + +TEXT ·mulWW(SB),NOSPLIT,$0 + JMP ·mulWW_g(SB) + +TEXT ·divWW(SB),NOSPLIT,$0 + JMP ·divWW_g(SB) + +TEXT ·addVV(SB),NOSPLIT,$0 + JMP ·addVV_g(SB) + +TEXT ·subVV(SB),NOSPLIT,$0 + JMP ·subVV_g(SB) + +TEXT ·addVW(SB),NOSPLIT,$0 + JMP ·addVW_g(SB) + +TEXT ·subVW(SB),NOSPLIT,$0 + JMP ·subVW_g(SB) + +TEXT ·shlVU(SB),NOSPLIT,$0 + JMP ·shlVU_g(SB) + +TEXT ·shrVU(SB),NOSPLIT,$0 + JMP ·shrVU_g(SB) + +TEXT ·mulAddVWW(SB),NOSPLIT,$0 + JMP ·mulAddVWW_g(SB) + +TEXT ·addMulVVW(SB),NOSPLIT,$0 + JMP ·addMulVVW_g(SB) + +TEXT ·divWVW(SB),NOSPLIT,$0 + JMP ·divWVW_g(SB) diff --git a/vendor/github.com/golang/go/src/math/big/arith_mipsx.s b/vendor/github.com/golang/go/src/math/big/arith_mipsx.s new file mode 100644 index 000000000000..54cafbd9c0c8 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/arith_mipsx.s @@ -0,0 +1,43 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !math_big_pure_go,mips !math_big_pure_go,mipsle + +#include "textflag.h" + +// This file provides fast assembly versions for the elementary +// arithmetic operations on vectors implemented in arith.go. + +TEXT ·mulWW(SB),NOSPLIT,$0 + JMP ·mulWW_g(SB) + +TEXT ·divWW(SB),NOSPLIT,$0 + JMP ·divWW_g(SB) + +TEXT ·addVV(SB),NOSPLIT,$0 + JMP ·addVV_g(SB) + +TEXT ·subVV(SB),NOSPLIT,$0 + JMP ·subVV_g(SB) + +TEXT ·addVW(SB),NOSPLIT,$0 + JMP ·addVW_g(SB) + +TEXT ·subVW(SB),NOSPLIT,$0 + JMP ·subVW_g(SB) + +TEXT ·shlVU(SB),NOSPLIT,$0 + JMP ·shlVU_g(SB) + +TEXT ·shrVU(SB),NOSPLIT,$0 + JMP ·shrVU_g(SB) + +TEXT ·mulAddVWW(SB),NOSPLIT,$0 + JMP ·mulAddVWW_g(SB) + +TEXT ·addMulVVW(SB),NOSPLIT,$0 + JMP ·addMulVVW_g(SB) + +TEXT ·divWVW(SB),NOSPLIT,$0 + JMP ·divWVW_g(SB) diff --git a/vendor/github.com/golang/go/src/math/big/arith_ppc64x.s b/vendor/github.com/golang/go/src/math/big/arith_ppc64x.s new file mode 100644 index 000000000000..74db48933f8a --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/arith_ppc64x.s @@ -0,0 +1,197 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !math_big_pure_go,ppc64 !math_big_pure_go,ppc64le + +#include "textflag.h" + +// This file provides fast assembly versions for the elementary +// arithmetic operations on vectors implemented in arith.go. + +// func mulWW(x, y Word) (z1, z0 Word) +TEXT ·mulWW(SB), NOSPLIT, $0 + MOVD x+0(FP), R4 + MOVD y+8(FP), R5 + MULHDU R4, R5, R6 + MULLD R4, R5, R7 + MOVD R6, z1+16(FP) + MOVD R7, z0+24(FP) + RET + +// func addVV(z, y, y []Word) (c Word) +// z[i] = x[i] + y[i] for all i, carrying +TEXT ·addVV(SB), NOSPLIT, $0 + MOVD z_len+8(FP), R7 + MOVD x+24(FP), R8 + MOVD y+48(FP), R9 + MOVD z+0(FP), R10 + + MOVD R0, R4 + MOVD R0, R6 // R6 will be the address index + ADDC R4, R4 // clear CA + MOVD R7, CTR + + CMP R0, R7 + BEQ done + +loop: + MOVD (R8)(R6), R11 // x[i] + MOVD (R9)(R6), R12 // y[i] + ADDE R12, R11, R15 // x[i] + y[i] + CA + MOVD R15, (R10)(R6) // z[i] + + ADD $8, R6 + BC 16, 0, loop // bdnz + +done: + ADDZE R4 + MOVD R4, c+72(FP) + RET + +// func subVV(z, x, y []Word) (c Word) +// z[i] = x[i] - y[i] for all i, carrying +TEXT ·subVV(SB), NOSPLIT, $0 + MOVD z_len+8(FP), R7 + MOVD x+24(FP), R8 + MOVD y+48(FP), R9 + MOVD z+0(FP), R10 + + MOVD R0, R4 // c = 0 + MOVD R0, R6 + SUBC R0, R0 // clear CA + MOVD R7, CTR + + CMP R0, R7 + BEQ sublend + +// amd64 saves and restores CF, but I believe they only have to do that because all of +// their math operations clobber it - we should just be able to recover it at the end. +subloop: + MOVD (R8)(R6), R11 // x[i] + MOVD (R9)(R6), R12 // y[i] + + SUBE R12, R11, R15 + MOVD R15, (R10)(R6) + + ADD $8, R6 + BC 16, 0, subloop // bdnz + +sublend: + + ADDZE R4 + XOR $1, R4 + MOVD R4, c+72(FP) + RET + +TEXT ·addVW(SB), NOSPLIT, $0 + BR ·addVW_g(SB) + +TEXT ·subVW(SB), NOSPLIT, $0 + BR ·subVW_g(SB) + +TEXT ·shlVU(SB), NOSPLIT, $0 + BR ·shlVU_g(SB) + +TEXT ·shrVU(SB), NOSPLIT, $0 + BR ·shrVU_g(SB) + +// func mulAddVWW(z, x []Word, y, r Word) (c Word) +TEXT ·mulAddVWW(SB), NOSPLIT, $0 + MOVD z+0(FP), R10 // R10 = z[] + MOVD x+24(FP), R8 // R8 = x[] + MOVD y+48(FP), R9 // R9 = y + MOVD r+56(FP), R4 // R4 = r = c + MOVD z_len+8(FP), R11 // R11 = z_len + + MOVD R0, R3 // R3 will be the index register + CMP R0, R11 + MOVD R11, CTR // Initialize loop counter + BEQ done + +loop: + MOVD (R8)(R3), R20 // x[i] + MULLD R9, R20, R6 // R6 = z0 = Low-order(x[i]*y) + MULHDU R9, R20, R7 // R7 = z1 = High-order(x[i]*y) + ADDC R4, R6 // Compute sum for z1 and z0 + ADDZE R7 + MOVD R6, (R10)(R3) // z[i] + MOVD R7, R4 // c + ADD $8, R3 + BC 16, 0, loop // bdnz + +done: + MOVD R4, c+64(FP) + RET + +// func addMulVVW(z, x []Word, y Word) (c Word) +TEXT ·addMulVVW(SB), NOSPLIT, $0 + MOVD z+0(FP), R10 // R10 = z[] + MOVD x+24(FP), R8 // R8 = x[] + MOVD y+48(FP), R9 // R9 = y + MOVD z_len+8(FP), R22 // R22 = z_len + + MOVD R0, R3 // R3 will be the index register + CMP R0, R22 + MOVD R0, R4 // R4 = c = 0 + MOVD R22, CTR // Initialize loop counter + BEQ done + +loop: + MOVD (R8)(R3), R20 // Load x[i] + MOVD (R10)(R3), R21 // Load z[i] + MULLD R9, R20, R6 // R6 = Low-order(x[i]*y) + MULHDU R9, R20, R7 // R7 = High-order(x[i]*y) + ADDC R21, R6 // R6 = z0 + ADDZE R7 // R7 = z1 + ADDC R4, R6 // R6 = z0 + c + 0 + ADDZE R7, R4 // c += z1 + MOVD R6, (R10)(R3) // Store z[i] + ADD $8, R3 + BC 16, 0, loop // bdnz + +done: + MOVD R4, c+56(FP) + RET + +// func divWW(x1, x0, y Word) (q, r Word) +TEXT ·divWW(SB), NOSPLIT, $0 + MOVD x1+0(FP), R4 + MOVD x0+8(FP), R5 + MOVD y+16(FP), R6 + + CMPU R4, R6 + BGE divbigger + + // from the programmer's note in ch. 3 of the ISA manual, p.74 + DIVDEU R6, R4, R3 + DIVDU R6, R5, R7 + MULLD R6, R3, R8 + MULLD R6, R7, R20 + SUB R20, R5, R10 + ADD R7, R3, R3 + SUB R8, R10, R4 + CMPU R4, R10 + BLT adjust + CMPU R4, R6 + BLT end + +adjust: + MOVD $1, R21 + ADD R21, R3, R3 + SUB R6, R4, R4 + +end: + MOVD R3, q+24(FP) + MOVD R4, r+32(FP) + + RET + +divbigger: + MOVD $-1, R7 + MOVD R7, q+24(FP) + MOVD R7, r+32(FP) + RET + +TEXT ·divWVW(SB), NOSPLIT, $0 + BR ·divWVW_g(SB) diff --git a/vendor/github.com/golang/go/src/math/big/arith_s390x.s b/vendor/github.com/golang/go/src/math/big/arith_s390x.s new file mode 100644 index 000000000000..4520d161d779 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/arith_s390x.s @@ -0,0 +1,1239 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !math_big_pure_go,s390x + +#include "textflag.h" + +// This file provides fast assembly versions for the elementary +// arithmetic operations on vectors implemented in arith.go. + +TEXT ·hasVectorFacility(SB),NOSPLIT,$24-1 + MOVD $x-24(SP), R1 + XC $24, 0(R1), 0(R1) // clear the storage + MOVD $2, R0 // R0 is the number of double words stored -1 + WORD $0xB2B01000 // STFLE 0(R1) + XOR R0, R0 // reset the value of R0 + MOVBZ z-8(SP), R1 + AND $0x40, R1 + BEQ novector +vectorinstalled: + // check if the vector instruction has been enabled + VLEIB $0, $0xF, V16 + VLGVB $0, V16, R1 + CMPBNE R1, $0xF, novector + MOVB $1, ret+0(FP) // have vx + RET +novector: + MOVB $0, ret+0(FP) // no vx + RET + +TEXT ·mulWW(SB),NOSPLIT,$0 + MOVD x+0(FP), R3 + MOVD y+8(FP), R4 + MULHDU R3, R4 + MOVD R10, z1+16(FP) + MOVD R11, z0+24(FP) + RET + +// func divWW(x1, x0, y Word) (q, r Word) +TEXT ·divWW(SB),NOSPLIT,$0 + MOVD x1+0(FP), R10 + MOVD x0+8(FP), R11 + MOVD y+16(FP), R5 + WORD $0xb98700a5 // dlgr r10,r5 + MOVD R11, q+24(FP) + MOVD R10, r+32(FP) + RET + +// DI = R3, CX = R4, SI = r10, r8 = r8, r9=r9, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0) + use R11 +// func addVV(z, x, y []Word) (c Word) + + +TEXT ·addVV(SB),NOSPLIT,$0 + MOVD addvectorfacility+0x00(SB),R1 + BR (R1) + +TEXT ·addVV_check(SB),NOSPLIT, $0 + MOVB ·hasVX(SB), R1 + CMPBEQ R1, $1, vectorimpl // vectorfacility = 1, vector supported + MOVD $addvectorfacility+0x00(SB), R1 + MOVD $·addVV_novec(SB), R2 + MOVD R2, 0(R1) + //MOVD $·addVV_novec(SB), 0(R1) + BR ·addVV_novec(SB) +vectorimpl: + MOVD $addvectorfacility+0x00(SB), R1 + MOVD $·addVV_vec(SB), R2 + MOVD R2, 0(R1) + //MOVD $·addVV_vec(SB), 0(R1) + BR ·addVV_vec(SB) + +GLOBL addvectorfacility+0x00(SB), NOPTR, $8 +DATA addvectorfacility+0x00(SB)/8, $·addVV_check(SB) + +TEXT ·addVV_vec(SB),NOSPLIT,$0 + MOVD z_len+8(FP), R3 + MOVD x+24(FP), R8 + MOVD y+48(FP), R9 + MOVD z+0(FP), R2 + + MOVD $0, R4 // c = 0 + MOVD $0, R0 // make sure it's zero + MOVD $0, R10 // i = 0 + + + // s/JL/JMP/ below to disable the unrolled loop + SUB $4, R3 + BLT v1 + SUB $12, R3 // n -= 16 + BLT A1 // if n < 0 goto A1 + + MOVD R8, R5 + MOVD R9, R6 + MOVD R2, R7 + // n >= 0 + // regular loop body unrolled 16x + VZERO V0 // c = 0 +UU1: VLM 0(R5), V1, V4 // 64-bytes into V1..V8 + ADD $64, R5 + VPDI $0x4,V1,V1,V1 // flip the doublewords to big-endian order + VPDI $0x4,V2,V2,V2 // flip the doublewords to big-endian order + + + VLM 0(R6), V9, V12 // 64-bytes into V9..V16 + ADD $64, R6 + VPDI $0x4,V9,V9,V9 // flip the doublewords to big-endian order + VPDI $0x4,V10,V10,V10 // flip the doublewords to big-endian order + + VACCCQ V1, V9, V0, V25 + VACQ V1, V9, V0, V17 + VACCCQ V2, V10, V25, V26 + VACQ V2, V10, V25, V18 + + + VLM 0(R5), V5, V6 // 32-bytes into V1..V8 + VLM 0(R6), V13, V14 // 32-bytes into V9..V16 + ADD $32, R5 + ADD $32, R6 + + VPDI $0x4,V3,V3,V3 // flip the doublewords to big-endian order + VPDI $0x4,V4,V4,V4 // flip the doublewords to big-endian order + VPDI $0x4,V11,V11,V11 // flip the doublewords to big-endian order + VPDI $0x4,V12,V12,V12 // flip the doublewords to big-endian order + + VACCCQ V3, V11, V26, V27 + VACQ V3, V11, V26, V19 + VACCCQ V4, V12, V27, V28 + VACQ V4, V12, V27, V20 + + VLM 0(R5), V7, V8 // 32-bytes into V1..V8 + VLM 0(R6), V15, V16 // 32-bytes into V9..V16 + ADD $32, R5 + ADD $32, R6 + + VPDI $0x4,V5,V5,V5 // flip the doublewords to big-endian order + VPDI $0x4,V6,V6,V6 // flip the doublewords to big-endian order + VPDI $0x4,V13,V13,V13 // flip the doublewords to big-endian order + VPDI $0x4,V14,V14,V14 // flip the doublewords to big-endian order + + VACCCQ V5, V13, V28, V29 + VACQ V5, V13, V28, V21 + VACCCQ V6, V14, V29, V30 + VACQ V6, V14, V29, V22 + + VPDI $0x4,V7,V7,V7 // flip the doublewords to big-endian order + VPDI $0x4,V8,V8,V8 // flip the doublewords to big-endian order + VPDI $0x4,V15,V15,V15 // flip the doublewords to big-endian order + VPDI $0x4,V16,V16,V16 // flip the doublewords to big-endian order + + VACCCQ V7, V15, V30, V31 + VACQ V7, V15, V30, V23 + VACCCQ V8, V16, V31, V0 //V0 has carry-over + VACQ V8, V16, V31, V24 + + VPDI $0x4,V17,V17,V17 // flip the doublewords to big-endian order + VPDI $0x4,V18,V18,V18 // flip the doublewords to big-endian order + VPDI $0x4,V19,V19,V19 // flip the doublewords to big-endian order + VPDI $0x4,V20,V20,V20 // flip the doublewords to big-endian order + VPDI $0x4,V21,V21,V21 // flip the doublewords to big-endian order + VPDI $0x4,V22,V22,V22 // flip the doublewords to big-endian order + VPDI $0x4,V23,V23,V23 // flip the doublewords to big-endian order + VPDI $0x4,V24,V24,V24 // flip the doublewords to big-endian order + VSTM V17, V24, 0(R7) // 128-bytes into z + ADD $128, R7 + ADD $128, R10 // i += 16 + SUB $16, R3 // n -= 16 + BGE UU1 // if n >= 0 goto U1 + VLGVG $1, V0, R4 // put cf into R4 + NEG R4, R4 // save cf + +A1: ADD $12, R3 // n += 16 + + + // s/JL/JMP/ below to disable the unrolled loop + BLT v1 // if n < 0 goto v1 + +U1: // n >= 0 + // regular loop body unrolled 4x + MOVD 0(R8)(R10*1), R5 + MOVD 8(R8)(R10*1), R6 + MOVD 16(R8)(R10*1), R7 + MOVD 24(R8)(R10*1), R1 + ADDC R4, R4 // restore CF + MOVD 0(R9)(R10*1), R11 + ADDE R11, R5 + MOVD 8(R9)(R10*1), R11 + ADDE R11, R6 + MOVD 16(R9)(R10*1), R11 + ADDE R11, R7 + MOVD 24(R9)(R10*1), R11 + ADDE R11, R1 + MOVD R0, R4 + ADDE R4, R4 // save CF + NEG R4, R4 + MOVD R5, 0(R2)(R10*1) + MOVD R6, 8(R2)(R10*1) + MOVD R7, 16(R2)(R10*1) + MOVD R1, 24(R2)(R10*1) + + + ADD $32, R10 // i += 4 + SUB $4, R3 // n -= 4 + BGE U1 // if n >= 0 goto U1 + +v1: ADD $4, R3 // n += 4 + BLE E1 // if n <= 0 goto E1 + +L1: // n > 0 + ADDC R4, R4 // restore CF + MOVD 0(R8)(R10*1), R5 + MOVD 0(R9)(R10*1), R11 + ADDE R11, R5 + MOVD R5, 0(R2)(R10*1) + MOVD R0, R4 + ADDE R4, R4 // save CF + NEG R4, R4 + + ADD $8, R10 // i++ + SUB $1, R3 // n-- + BGT L1 // if n > 0 goto L1 + +E1: NEG R4, R4 + MOVD R4, c+72(FP) // return c + RET + +TEXT ·addVV_novec(SB),NOSPLIT,$0 +novec: + MOVD z_len+8(FP), R3 + MOVD x+24(FP), R8 + MOVD y+48(FP), R9 + MOVD z+0(FP), R2 + + MOVD $0, R4 // c = 0 + MOVD $0, R0 // make sure it's zero + MOVD $0, R10 // i = 0 + + // s/JL/JMP/ below to disable the unrolled loop + SUB $4, R3 // n -= 4 + BLT v1n // if n < 0 goto v1n +U1n: // n >= 0 + // regular loop body unrolled 4x + MOVD 0(R8)(R10*1), R5 + MOVD 8(R8)(R10*1), R6 + MOVD 16(R8)(R10*1), R7 + MOVD 24(R8)(R10*1), R1 + ADDC R4, R4 // restore CF + MOVD 0(R9)(R10*1), R11 + ADDE R11, R5 + MOVD 8(R9)(R10*1), R11 + ADDE R11, R6 + MOVD 16(R9)(R10*1), R11 + ADDE R11, R7 + MOVD 24(R9)(R10*1), R11 + ADDE R11, R1 + MOVD R0, R4 + ADDE R4, R4 // save CF + NEG R4, R4 + MOVD R5, 0(R2)(R10*1) + MOVD R6, 8(R2)(R10*1) + MOVD R7, 16(R2)(R10*1) + MOVD R1, 24(R2)(R10*1) + + + ADD $32, R10 // i += 4 + SUB $4, R3 // n -= 4 + BGE U1n // if n >= 0 goto U1n + +v1n: ADD $4, R3 // n += 4 + BLE E1n // if n <= 0 goto E1n + +L1n: // n > 0 + ADDC R4, R4 // restore CF + MOVD 0(R8)(R10*1), R5 + MOVD 0(R9)(R10*1), R11 + ADDE R11, R5 + MOVD R5, 0(R2)(R10*1) + MOVD R0, R4 + ADDE R4, R4 // save CF + NEG R4, R4 + + ADD $8, R10 // i++ + SUB $1, R3 // n-- + BGT L1n // if n > 0 goto L1n + +E1n: NEG R4, R4 + MOVD R4, c+72(FP) // return c + RET + + +TEXT ·subVV(SB),NOSPLIT,$0 + MOVD subvectorfacility+0x00(SB),R1 + BR (R1) + +TEXT ·subVV_check(SB),NOSPLIT,$0 + MOVB ·hasVX(SB), R1 + CMPBEQ R1, $1, vectorimpl // vectorfacility = 1, vector supported + MOVD $subvectorfacility+0x00(SB), R1 + MOVD $·subVV_novec(SB), R2 + MOVD R2, 0(R1) + //MOVD $·subVV_novec(SB), 0(R1) + BR ·subVV_novec(SB) +vectorimpl: + MOVD $subvectorfacility+0x00(SB), R1 + MOVD $·subVV_vec(SB), R2 + MOVD R2, 0(R1) + //MOVD $·subVV_vec(SB), 0(R1) + BR ·subVV_vec(SB) + +GLOBL subvectorfacility+0x00(SB), NOPTR, $8 +DATA subvectorfacility+0x00(SB)/8, $·subVV_check(SB) + +// DI = R3, CX = R4, SI = r10, r8 = r8, r9=r9, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0) + use R11 +// func subVV(z, x, y []Word) (c Word) +// (same as addVV except for SUBC/SUBE instead of ADDC/ADDE and label names) +TEXT ·subVV_vec(SB),NOSPLIT,$0 + MOVD z_len+8(FP), R3 + MOVD x+24(FP), R8 + MOVD y+48(FP), R9 + MOVD z+0(FP), R2 + MOVD $0, R4 // c = 0 + MOVD $0, R0 // make sure it's zero + MOVD $0, R10 // i = 0 + + // s/JL/JMP/ below to disable the unrolled loop + SUB $4, R3 // n -= 4 + BLT v1 // if n < 0 goto v1 + SUB $12, R3 // n -= 16 + BLT A1 // if n < 0 goto A1 + + MOVD R8, R5 + MOVD R9, R6 + MOVD R2, R7 + + // n >= 0 + // regular loop body unrolled 16x + VZERO V0 // cf = 0 + MOVD $1, R4 // for 390 subtraction cf starts as 1 (no borrow) + VLVGG $1, R4, V0 //put carry into V0 + +UU1: VLM 0(R5), V1, V4 // 64-bytes into V1..V8 + ADD $64, R5 + VPDI $0x4,V1,V1,V1 // flip the doublewords to big-endian order + VPDI $0x4,V2,V2,V2 // flip the doublewords to big-endian order + + + VLM 0(R6), V9, V12 // 64-bytes into V9..V16 + ADD $64, R6 + VPDI $0x4,V9,V9,V9 // flip the doublewords to big-endian order + VPDI $0x4,V10,V10,V10 // flip the doublewords to big-endian order + + VSBCBIQ V1, V9, V0, V25 + VSBIQ V1, V9, V0, V17 + VSBCBIQ V2, V10, V25, V26 + VSBIQ V2, V10, V25, V18 + + + VLM 0(R5), V5, V6 // 32-bytes into V1..V8 + VLM 0(R6), V13, V14 // 32-bytes into V9..V16 + ADD $32, R5 + ADD $32, R6 + + VPDI $0x4,V3,V3,V3 // flip the doublewords to big-endian order + VPDI $0x4,V4,V4,V4 // flip the doublewords to big-endian order + VPDI $0x4,V11,V11,V11 // flip the doublewords to big-endian order + VPDI $0x4,V12,V12,V12 // flip the doublewords to big-endian order + + VSBCBIQ V3, V11, V26, V27 + VSBIQ V3, V11, V26, V19 + VSBCBIQ V4, V12, V27, V28 + VSBIQ V4, V12, V27, V20 + + VLM 0(R5), V7, V8 // 32-bytes into V1..V8 + VLM 0(R6), V15, V16 // 32-bytes into V9..V16 + ADD $32, R5 + ADD $32, R6 + + VPDI $0x4,V5,V5,V5 // flip the doublewords to big-endian order + VPDI $0x4,V6,V6,V6 // flip the doublewords to big-endian order + VPDI $0x4,V13,V13,V13 // flip the doublewords to big-endian order + VPDI $0x4,V14,V14,V14 // flip the doublewords to big-endian order + + VSBCBIQ V5, V13, V28, V29 + VSBIQ V5, V13, V28, V21 + VSBCBIQ V6, V14, V29, V30 + VSBIQ V6, V14, V29, V22 + + VPDI $0x4,V7,V7,V7 // flip the doublewords to big-endian order + VPDI $0x4,V8,V8,V8 // flip the doublewords to big-endian order + VPDI $0x4,V15,V15,V15 // flip the doublewords to big-endian order + VPDI $0x4,V16,V16,V16 // flip the doublewords to big-endian order + + VSBCBIQ V7, V15, V30, V31 + VSBIQ V7, V15, V30, V23 + VSBCBIQ V8, V16, V31, V0 //V0 has carry-over + VSBIQ V8, V16, V31, V24 + + VPDI $0x4,V17,V17,V17 // flip the doublewords to big-endian order + VPDI $0x4,V18,V18,V18 // flip the doublewords to big-endian order + VPDI $0x4,V19,V19,V19 // flip the doublewords to big-endian order + VPDI $0x4,V20,V20,V20 // flip the doublewords to big-endian order + VPDI $0x4,V21,V21,V21 // flip the doublewords to big-endian order + VPDI $0x4,V22,V22,V22 // flip the doublewords to big-endian order + VPDI $0x4,V23,V23,V23 // flip the doublewords to big-endian order + VPDI $0x4,V24,V24,V24 // flip the doublewords to big-endian order + VSTM V17, V24, 0(R7) // 128-bytes into z + ADD $128, R7 + ADD $128, R10 // i += 16 + SUB $16, R3 // n -= 16 + BGE UU1 // if n >= 0 goto U1 + VLGVG $1, V0, R4 // put cf into R4 + SUB $1, R4 // save cf + +A1: ADD $12, R3 // n += 16 + BLT v1 // if n < 0 goto v1 + +U1: // n >= 0 + // regular loop body unrolled 4x + MOVD 0(R8)(R10*1), R5 + MOVD 8(R8)(R10*1), R6 + MOVD 16(R8)(R10*1), R7 + MOVD 24(R8)(R10*1), R1 + MOVD R0, R11 + SUBC R4, R11 // restore CF + MOVD 0(R9)(R10*1), R11 + SUBE R11, R5 + MOVD 8(R9)(R10*1), R11 + SUBE R11, R6 + MOVD 16(R9)(R10*1), R11 + SUBE R11, R7 + MOVD 24(R9)(R10*1), R11 + SUBE R11, R1 + MOVD R0, R4 + SUBE R4, R4 // save CF + MOVD R5, 0(R2)(R10*1) + MOVD R6, 8(R2)(R10*1) + MOVD R7, 16(R2)(R10*1) + MOVD R1, 24(R2)(R10*1) + + ADD $32, R10 // i += 4 + SUB $4, R3 // n -= 4 + BGE U1 // if n >= 0 goto U1n + +v1: ADD $4, R3 // n += 4 + BLE E1 // if n <= 0 goto E1 + +L1: // n > 0 + MOVD R0, R11 + SUBC R4, R11 // restore CF + MOVD 0(R8)(R10*1), R5 + MOVD 0(R9)(R10*1), R11 + SUBE R11, R5 + MOVD R5, 0(R2)(R10*1) + MOVD R0, R4 + SUBE R4, R4 // save CF + + ADD $8, R10 // i++ + SUB $1, R3 // n-- + BGT L1 // if n > 0 goto L1n + +E1: NEG R4, R4 + MOVD R4, c+72(FP) // return c + RET + + +// DI = R3, CX = R4, SI = r10, r8 = r8, r9=r9, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0) + use R11 +// func subVV(z, x, y []Word) (c Word) +// (same as addVV except for SUBC/SUBE instead of ADDC/ADDE and label names) +TEXT ·subVV_novec(SB),NOSPLIT,$0 + MOVD z_len+8(FP), R3 + MOVD x+24(FP), R8 + MOVD y+48(FP), R9 + MOVD z+0(FP), R2 + + MOVD $0, R4 // c = 0 + MOVD $0, R0 // make sure it's zero + MOVD $0, R10 // i = 0 + + // s/JL/JMP/ below to disable the unrolled loop + SUB $4, R3 // n -= 4 + BLT v1 // if n < 0 goto v1 + +U1: // n >= 0 + // regular loop body unrolled 4x + MOVD 0(R8)(R10*1), R5 + MOVD 8(R8)(R10*1), R6 + MOVD 16(R8)(R10*1), R7 + MOVD 24(R8)(R10*1), R1 + MOVD R0, R11 + SUBC R4, R11 // restore CF + MOVD 0(R9)(R10*1), R11 + SUBE R11, R5 + MOVD 8(R9)(R10*1), R11 + SUBE R11, R6 + MOVD 16(R9)(R10*1), R11 + SUBE R11, R7 + MOVD 24(R9)(R10*1), R11 + SUBE R11, R1 + MOVD R0, R4 + SUBE R4, R4 // save CF + MOVD R5, 0(R2)(R10*1) + MOVD R6, 8(R2)(R10*1) + MOVD R7, 16(R2)(R10*1) + MOVD R1, 24(R2)(R10*1) + + + ADD $32, R10 // i += 4 + SUB $4, R3 // n -= 4 + BGE U1 // if n >= 0 goto U1 + +v1: ADD $4, R3 // n += 4 + BLE E1 // if n <= 0 goto E1 + +L1: // n > 0 + MOVD R0, R11 + SUBC R4, R11 // restore CF + MOVD 0(R8)(R10*1), R5 + MOVD 0(R9)(R10*1), R11 + SUBE R11, R5 + MOVD R5, 0(R2)(R10*1) + MOVD R0, R4 + SUBE R4, R4 // save CF + + ADD $8, R10 // i++ + SUB $1, R3 // n-- + BGT L1 // if n > 0 goto L1 + +E1: NEG R4, R4 + MOVD R4, c+72(FP) // return c + RET + +TEXT ·addVW(SB),NOSPLIT,$0 + MOVD addwvectorfacility+0x00(SB),R1 + BR (R1) + +TEXT ·addVW_check(SB),NOSPLIT,$0 + MOVB ·hasVX(SB), R1 + CMPBEQ R1, $1, vectorimpl // vectorfacility = 1, vector supported + MOVD $addwvectorfacility+0x00(SB), R1 + MOVD $·addVW_novec(SB), R2 + MOVD R2, 0(R1) + //MOVD $·addVW_novec(SB), 0(R1) + BR ·addVW_novec(SB) +vectorimpl: + MOVD $addwvectorfacility+0x00(SB), R1 + MOVD $·addVW_vec(SB), R2 + MOVD R2, 0(R1) + //MOVD $·addVW_vec(SB), 0(R1) + BR ·addVW_vec(SB) + +GLOBL addwvectorfacility+0x00(SB), NOPTR, $8 +DATA addwvectorfacility+0x00(SB)/8, $·addVW_check(SB) + + +// func addVW_vec(z, x []Word, y Word) (c Word) +TEXT ·addVW_vec(SB),NOSPLIT,$0 + MOVD z_len+8(FP), R3 + MOVD x+24(FP), R8 + MOVD y+48(FP), R4 // c = y + MOVD z+0(FP), R2 + + MOVD $0, R0 // make sure it's zero + MOVD $0, R10 // i = 0 + MOVD R8, R5 + MOVD R2, R7 + + // s/JL/JMP/ below to disable the unrolled loop + SUB $4, R3 // n -= 4 + BLT v10 // if n < 0 goto v10 + SUB $12, R3 + BLT A10 + + // n >= 0 + // regular loop body unrolled 16x + + VZERO V0 // prepare V0 to be final carry register + VZERO V9 // to ensure upper half is zero + VLVGG $1, R4, V9 +UU1: VLM 0(R5), V1, V4 // 64-bytes into V1..V4 + ADD $64, R5 + VPDI $0x4,V1,V1,V1 // flip the doublewords to big-endian order + VPDI $0x4,V2,V2,V2 // flip the doublewords to big-endian order + + + VACCCQ V1, V9, V0, V25 + VACQ V1, V9, V0, V17 + VZERO V9 + VACCCQ V2, V9, V25, V26 + VACQ V2, V9, V25, V18 + + + VLM 0(R5), V5, V6 // 32-bytes into V5..V6 + ADD $32, R5 + + VPDI $0x4,V3,V3,V3 // flip the doublewords to big-endian order + VPDI $0x4,V4,V4,V4 // flip the doublewords to big-endian order + + VACCCQ V3, V9, V26, V27 + VACQ V3, V9, V26, V19 + VACCCQ V4, V9, V27, V28 + VACQ V4, V9, V27, V20 + + VLM 0(R5), V7, V8 // 32-bytes into V7..V8 + ADD $32, R5 + + VPDI $0x4,V5,V5,V5 // flip the doublewords to big-endian order + VPDI $0x4,V6,V6,V6 // flip the doublewords to big-endian order + + VACCCQ V5, V9, V28, V29 + VACQ V5, V9, V28, V21 + VACCCQ V6, V9, V29, V30 + VACQ V6, V9, V29, V22 + + VPDI $0x4,V7,V7,V7 // flip the doublewords to big-endian order + VPDI $0x4,V8,V8,V8 // flip the doublewords to big-endian order + + VACCCQ V7, V9, V30, V31 + VACQ V7, V9, V30, V23 + VACCCQ V8, V9, V31, V0 //V0 has carry-over + VACQ V8, V9, V31, V24 + + VPDI $0x4,V17,V17,V17 // flip the doublewords to big-endian order + VPDI $0x4,V18,V18,V18 // flip the doublewords to big-endian order + VPDI $0x4,V19,V19,V19 // flip the doublewords to big-endian order + VPDI $0x4,V20,V20,V20 // flip the doublewords to big-endian order + VPDI $0x4,V21,V21,V21 // flip the doublewords to big-endian order + VPDI $0x4,V22,V22,V22 // flip the doublewords to big-endian order + VPDI $0x4,V23,V23,V23 // flip the doublewords to big-endian order + VPDI $0x4,V24,V24,V24 // flip the doublewords to big-endian order + VSTM V17, V24, 0(R7) // 128-bytes into z + ADD $128, R7 + ADD $128, R10 // i += 16 + SUB $16, R3 // n -= 16 + BGE UU1 // if n >= 0 goto U1 + VLGVG $1, V0, R4 // put cf into R4 in case we branch to v10 + +A10: ADD $12, R3 // n += 16 + + + // s/JL/JMP/ below to disable the unrolled loop + + BLT v10 // if n < 0 goto v10 + + +U4: // n >= 0 + // regular loop body unrolled 4x + MOVD 0(R8)(R10*1), R5 + MOVD 8(R8)(R10*1), R6 + MOVD 16(R8)(R10*1), R7 + MOVD 24(R8)(R10*1), R1 + ADDC R4, R5 + ADDE R0, R6 + ADDE R0, R7 + ADDE R0, R1 + ADDE R0, R0 + MOVD R0, R4 // save CF + SUB R0, R0 + MOVD R5, 0(R2)(R10*1) + MOVD R6, 8(R2)(R10*1) + MOVD R7, 16(R2)(R10*1) + MOVD R1, 24(R2)(R10*1) + + ADD $32, R10 // i += 4 -> i +=32 + SUB $4, R3 // n -= 4 + BGE U4 // if n >= 0 goto U4 + +v10: ADD $4, R3 // n += 4 + BLE E10 // if n <= 0 goto E4 + + +L4: // n > 0 + MOVD 0(R8)(R10*1), R5 + ADDC R4, R5 + ADDE R0, R0 + MOVD R0, R4 // save CF + SUB R0, R0 + MOVD R5, 0(R2)(R10*1) + + ADD $8, R10 // i++ + SUB $1, R3 // n-- + BGT L4 // if n > 0 goto L4 + +E10: MOVD R4, c+56(FP) // return c + + RET + + +TEXT ·addVW_novec(SB),NOSPLIT,$0 +//DI = R3, CX = R4, SI = r10, r8 = r8, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0) + MOVD z_len+8(FP), R3 + MOVD x+24(FP), R8 + MOVD y+48(FP), R4 // c = y + MOVD z+0(FP), R2 + MOVD $0, R0 // make sure it's 0 + MOVD $0, R10 // i = 0 + + // s/JL/JMP/ below to disable the unrolled loop + SUB $4, R3 // n -= 4 + BLT v4 // if n < 4 goto v4 + +U4: // n >= 0 + // regular loop body unrolled 4x + MOVD 0(R8)(R10*1), R5 + MOVD 8(R8)(R10*1), R6 + MOVD 16(R8)(R10*1), R7 + MOVD 24(R8)(R10*1), R1 + ADDC R4, R5 + ADDE R0, R6 + ADDE R0, R7 + ADDE R0, R1 + ADDE R0, R0 + MOVD R0, R4 // save CF + SUB R0, R0 + MOVD R5, 0(R2)(R10*1) + MOVD R6, 8(R2)(R10*1) + MOVD R7, 16(R2)(R10*1) + MOVD R1, 24(R2)(R10*1) + + ADD $32, R10 // i += 4 -> i +=32 + SUB $4, R3 // n -= 4 + BGE U4 // if n >= 0 goto U4 + +v4: ADD $4, R3 // n += 4 + BLE E4 // if n <= 0 goto E4 + +L4: // n > 0 + MOVD 0(R8)(R10*1), R5 + ADDC R4, R5 + ADDE R0, R0 + MOVD R0, R4 // save CF + SUB R0, R0 + MOVD R5, 0(R2)(R10*1) + + ADD $8, R10 // i++ + SUB $1, R3 // n-- + BGT L4 // if n > 0 goto L4 + +E4: MOVD R4, c+56(FP) // return c + + RET + +TEXT ·subVW(SB),NOSPLIT,$0 + MOVD subwvectorfacility+0x00(SB),R1 + BR (R1) + +TEXT ·subVW_check(SB),NOSPLIT,$0 + MOVB ·hasVX(SB), R1 + CMPBEQ R1, $1, vectorimpl // vectorfacility = 1, vector supported + MOVD $subwvectorfacility+0x00(SB), R1 + MOVD $·subVW_novec(SB), R2 + MOVD R2, 0(R1) + //MOVD $·subVW_novec(SB), 0(R1) + BR ·subVW_novec(SB) +vectorimpl: + MOVD $subwvectorfacility+0x00(SB), R1 + MOVD $·subVW_vec(SB), R2 + MOVD R2, 0(R1) + //MOVD $·subVW_vec(SB), 0(R1) + BR ·subVW_vec(SB) + +GLOBL subwvectorfacility+0x00(SB), NOPTR, $8 +DATA subwvectorfacility+0x00(SB)/8, $·subVW_check(SB) + +// func subVW(z, x []Word, y Word) (c Word) +TEXT ·subVW_vec(SB),NOSPLIT,$0 + MOVD z_len+8(FP), R3 + MOVD x+24(FP), R8 + MOVD y+48(FP), R4 // c = y + MOVD z+0(FP), R2 + + MOVD $0, R0 // make sure it's zero + MOVD $0, R10 // i = 0 + MOVD R8, R5 + MOVD R2, R7 + + // s/JL/JMP/ below to disable the unrolled loop + SUB $4, R3 // n -= 4 + BLT v11 // if n < 0 goto v11 + SUB $12, R3 + BLT A11 + + VZERO V0 + MOVD $1, R6 // prepare V0 to be final carry register + VLVGG $1, R6, V0 // borrow is initially "no borrow" + VZERO V9 // to ensure upper half is zero + VLVGG $1, R4, V9 + + // n >= 0 + // regular loop body unrolled 16x + + +UU1: VLM 0(R5), V1, V4 // 64-bytes into V1..V4 + ADD $64, R5 + VPDI $0x4,V1,V1,V1 // flip the doublewords to big-endian order + VPDI $0x4,V2,V2,V2 // flip the doublewords to big-endian order + + + VSBCBIQ V1, V9, V0, V25 + VSBIQ V1, V9, V0, V17 + VZERO V9 + VSBCBIQ V2, V9, V25, V26 + VSBIQ V2, V9, V25, V18 + + VLM 0(R5), V5, V6 // 32-bytes into V5..V6 + ADD $32, R5 + + VPDI $0x4,V3,V3,V3 // flip the doublewords to big-endian order + VPDI $0x4,V4,V4,V4 // flip the doublewords to big-endian order + + + VSBCBIQ V3, V9, V26, V27 + VSBIQ V3, V9, V26, V19 + VSBCBIQ V4, V9, V27, V28 + VSBIQ V4, V9, V27, V20 + + VLM 0(R5), V7, V8 // 32-bytes into V7..V8 + ADD $32, R5 + + VPDI $0x4,V5,V5,V5 // flip the doublewords to big-endian order + VPDI $0x4,V6,V6,V6 // flip the doublewords to big-endian order + + VSBCBIQ V5, V9, V28, V29 + VSBIQ V5, V9, V28, V21 + VSBCBIQ V6, V9, V29, V30 + VSBIQ V6, V9, V29, V22 + + VPDI $0x4,V7,V7,V7 // flip the doublewords to big-endian order + VPDI $0x4,V8,V8,V8 // flip the doublewords to big-endian order + + VSBCBIQ V7, V9, V30, V31 + VSBIQ V7, V9, V30, V23 + VSBCBIQ V8, V9, V31, V0 // V0 has carry-over + VSBIQ V8, V9, V31, V24 + + VPDI $0x4,V17,V17,V17 // flip the doublewords to big-endian order + VPDI $0x4,V18,V18,V18 // flip the doublewords to big-endian order + VPDI $0x4,V19,V19,V19 // flip the doublewords to big-endian order + VPDI $0x4,V20,V20,V20 // flip the doublewords to big-endian order + VPDI $0x4,V21,V21,V21 // flip the doublewords to big-endian order + VPDI $0x4,V22,V22,V22 // flip the doublewords to big-endian order + VPDI $0x4,V23,V23,V23 // flip the doublewords to big-endian order + VPDI $0x4,V24,V24,V24 // flip the doublewords to big-endian order + VSTM V17, V24, 0(R7) // 128-bytes into z + ADD $128, R7 + ADD $128, R10 // i += 16 + SUB $16, R3 // n -= 16 + BGE UU1 // if n >= 0 goto U1 + VLGVG $1, V0, R4 // put cf into R4 in case we branch to v10 + SUB $1, R4 // save cf + NEG R4, R4 +A11: ADD $12, R3 // n += 16 + + BLT v11 // if n < 0 goto v11 + + // n >= 0 + // regular loop body unrolled 4x + +U4: // n >= 0 + // regular loop body unrolled 4x + MOVD 0(R8)(R10*1), R5 + MOVD 8(R8)(R10*1), R6 + MOVD 16(R8)(R10*1), R7 + MOVD 24(R8)(R10*1), R1 + SUBC R4, R5 //SLGR -> SUBC + SUBE R0, R6 //SLBGR -> SUBE + SUBE R0, R7 + SUBE R0, R1 + SUBE R4, R4 // save CF + NEG R4, R4 + MOVD R5, 0(R2)(R10*1) + MOVD R6, 8(R2)(R10*1) + MOVD R7, 16(R2)(R10*1) + MOVD R1, 24(R2)(R10*1) + + ADD $32, R10 // i += 4 -> i +=32 + SUB $4, R3 // n -= 4 + BGE U4 // if n >= 0 goto U4 + +v11: ADD $4, R3 // n += 4 + BLE E11 // if n <= 0 goto E4 + +L4: // n > 0 + + MOVD 0(R8)(R10*1), R5 + SUBC R4, R5 + SUBE R4, R4 // save CF + NEG R4, R4 + MOVD R5, 0(R2)(R10*1) + + ADD $8, R10 // i++ + SUB $1, R3 // n-- + BGT L4 // if n > 0 goto L4 + +E11: MOVD R4, c+56(FP) // return c + + RET + +//DI = R3, CX = R4, SI = r10, r8 = r8, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0) +// func subVW(z, x []Word, y Word) (c Word) +// (same as addVW except for SUBC/SUBE instead of ADDC/ADDE and label names) +TEXT ·subVW_novec(SB),NOSPLIT,$0 + MOVD z_len+8(FP), R3 + MOVD x+24(FP), R8 + MOVD y+48(FP), R4 // c = y + MOVD z+0(FP), R2 + MOVD $0, R0 // make sure it's 0 + MOVD $0, R10 // i = 0 + + // s/JL/JMP/ below to disable the unrolled loop + SUB $4, R3 // n -= 4 + BLT v4 // if n < 4 goto v4 + +U4: // n >= 0 + // regular loop body unrolled 4x + MOVD 0(R8)(R10*1), R5 + MOVD 8(R8)(R10*1), R6 + MOVD 16(R8)(R10*1), R7 + MOVD 24(R8)(R10*1), R1 + SUBC R4, R5 //SLGR -> SUBC + SUBE R0, R6 //SLBGR -> SUBE + SUBE R0, R7 + SUBE R0, R1 + SUBE R4, R4 // save CF + NEG R4, R4 + MOVD R5, 0(R2)(R10*1) + MOVD R6, 8(R2)(R10*1) + MOVD R7, 16(R2)(R10*1) + MOVD R1, 24(R2)(R10*1) + + ADD $32, R10 // i += 4 -> i +=32 + SUB $4, R3 // n -= 4 + BGE U4 // if n >= 0 goto U4 + +v4: ADD $4, R3 // n += 4 + BLE E4 // if n <= 0 goto E4 + +L4: // n > 0 + MOVD 0(R8)(R10*1), R5 + SUBC R4, R5 + SUBE R4, R4 // save CF + NEG R4, R4 + MOVD R5, 0(R2)(R10*1) + + ADD $8, R10 // i++ + SUB $1, R3 // n-- + BGT L4 // if n > 0 goto L4 + +E4: MOVD R4, c+56(FP) // return c + + RET + +// func shlVU(z, x []Word, s uint) (c Word) +TEXT ·shlVU(SB),NOSPLIT,$0 + MOVD z_len+8(FP), R5 + MOVD $0, R0 + SUB $1, R5 // n-- + BLT X8b // n < 0 (n <= 0) + + // n > 0 + MOVD s+48(FP), R4 + CMPBEQ R0, R4, Z80 //handle 0 case beq + MOVD $64, R6 + CMPBEQ R6, R4, Z864 //handle 64 case beq + MOVD z+0(FP), R2 + MOVD x+24(FP), R8 + SLD $3, R5 // n = n*8 + SUB R4, R6, R7 + MOVD (R8)(R5*1), R10 // w1 = x[i-1] + SRD R7, R10, R3 + MOVD R3, c+56(FP) + + MOVD $0, R1 // i = 0 + BR E8 + + // i < n-1 +L8: MOVD R10, R3 // w = w1 + MOVD -8(R8)(R5*1), R10 // w1 = x[i+1] + + SLD R4, R3 // w<>ŝ + SRD R7, R10, R6 + OR R6, R3 + MOVD R3, (R2)(R5*1) // z[i] = w<>ŝ + SUB $8, R5 // i-- + +E8: CMPBGT R5, R0, L8 // i < n-1 + + // i >= n-1 +X8a: SLD R4, R10 // w1<= n-1 + MOVD R10, (R2)(R5*1) + RET + +Z864: MOVD z+0(FP), R2 + MOVD x+24(FP), R8 + SLD $3, R5 // n = n*8 + MOVD (R8)(R5*1), R3 // w1 = x[n-1] + MOVD R3, c+56(FP) // z[i] = x[n-1] + + BR E864 + + // i < n-1 +L864: MOVD -8(R8)(R5*1), R3 + + MOVD R3, (R2)(R5*1) // z[i] = x[n-1] + SUB $8, R5 // i-- + +E864: CMPBGT R5, R0, L864 // i < n-1 + + MOVD R0, (R2) // z[n-1] = 0 + RET + + +// CX = R4, r8 = r8, r10 = r2 , r11 = r5, DX = r3, AX = r10 , BX = R1 , 64-count = r7 (R0 set to 0) temp = R6 +// func shrVU(z, x []Word, s uint) (c Word) +TEXT ·shrVU(SB),NOSPLIT,$0 + MOVD z_len+8(FP), R5 + MOVD $0, R0 + SUB $1, R5 // n-- + BLT X9b // n < 0 (n <= 0) + + // n > 0 + MOVD s+48(FP), R4 + CMPBEQ R0, R4, ZB0 //handle 0 case beq + MOVD $64, R6 + CMPBEQ R6, R4, ZB64 //handle 64 case beq + MOVD z+0(FP), R2 + MOVD x+24(FP), R8 + SLD $3, R5 // n = n*8 + SUB R4, R6, R7 + MOVD (R8), R10 // w1 = x[0] + SLD R7, R10, R3 + MOVD R3, c+56(FP) + + MOVD $0, R1 // i = 0 + BR E9 + + // i < n-1 +L9: MOVD R10, R3 // w = w1 + MOVD 8(R8)(R1*1), R10 // w1 = x[i+1] + + SRD R4, R3 // w>>s | w1<>s | w1<= n-1 +X9a: SRD R4, R10 // w1>>s + MOVD R10, (R2)(R5*1) // z[n-1] = w1>>s + RET + +X9b: MOVD R0, c+56(FP) + RET + +ZB0: MOVD z+0(FP), R2 + MOVD x+24(FP), R8 + SLD $3, R5 // n = n*8 + + MOVD (R8), R10 // w1 = x[0] + MOVD $0, R3 // R10 << 64 + MOVD R3, c+56(FP) + + MOVD $0, R1 // i = 0 + BR E9Z + + // i < n-1 +L9Z: MOVD R10, R3 // w = w1 + MOVD 8(R8)(R1*1), R10 // w1 = x[i+1] + + MOVD R3, (R2)(R1*1) // z[i] = w>>s | w1<= n-1 + MOVD R10, (R2)(R5*1) // z[n-1] = w1>>s + RET + +ZB64: MOVD z+0(FP), R2 + MOVD x+24(FP), R8 + SLD $3, R5 // n = n*8 + MOVD (R8), R3 // w1 = x[0] + MOVD R3, c+56(FP) + + MOVD $0, R1 // i = 0 + BR E964 + + // i < n-1 +L964: MOVD 8(R8)(R1*1), R3 // w1 = x[i+1] + + MOVD R3, (R2)(R1*1) // z[i] = w>>s | w1<= n-1 + MOVD $0, R10 // w1>>s + MOVD R10, (R2)(R5*1) // z[n-1] = w1>>s + RET + +// CX = R4, r8 = r8, r9=r9, r10 = r2 , r11 = r5, DX = r3, AX = r6 , BX = R1 , (R0 set to 0) + use R11 + use R7 for i +// func mulAddVWW(z, x []Word, y, r Word) (c Word) +TEXT ·mulAddVWW(SB),NOSPLIT,$0 + MOVD z+0(FP), R2 + MOVD x+24(FP), R8 + MOVD y+48(FP), R9 + MOVD r+56(FP), R4 // c = r + MOVD z_len+8(FP), R5 + MOVD $0, R1 // i = 0 + MOVD $0, R7 // i*8 = 0 + MOVD $0, R0 // make sure it's zero + BR E5 + +L5: MOVD (R8)(R1*1), R6 + MULHDU R9, R6 + ADDC R4, R11 //add to low order bits + ADDE R0, R6 + MOVD R11, (R2)(R1*1) + MOVD R6, R4 + ADD $8, R1 // i*8 + 8 + ADD $1, R7 // i++ + +E5: CMPBLT R7, R5, L5 // i < n + + MOVD R4, c+64(FP) + RET + +// func addMulVVW(z, x []Word, y Word) (c Word) +// CX = R4, r8 = r8, r9=r9, r10 = r2 , r11 = r5, AX = r11, DX = R6, r12=r12, BX = R1 , (R0 set to 0) + use R11 + use R7 for i +TEXT ·addMulVVW(SB),NOSPLIT,$0 + MOVD z+0(FP), R2 + MOVD x+24(FP), R8 + MOVD y+48(FP), R9 + MOVD z_len+8(FP), R5 + + MOVD $0, R1 // i*8 = 0 + MOVD $0, R7 // i = 0 + MOVD $0, R0 // make sure it's zero + MOVD $0, R4 // c = 0 + + MOVD R5, R12 + AND $-2, R12 + CMPBGE R5, $2, A6 + BR E6 + +A6: MOVD (R8)(R1*1), R6 + MULHDU R9, R6 + MOVD (R2)(R1*1), R10 + ADDC R10, R11 //add to low order bits + ADDE R0, R6 + ADDC R4, R11 + ADDE R0, R6 + MOVD R6, R4 + MOVD R11, (R2)(R1*1) + + MOVD (8)(R8)(R1*1), R6 + MULHDU R9, R6 + MOVD (8)(R2)(R1*1), R10 + ADDC R10, R11 //add to low order bits + ADDE R0, R6 + ADDC R4, R11 + ADDE R0, R6 + MOVD R6, R4 + MOVD R11, (8)(R2)(R1*1) + + ADD $16, R1 // i*8 + 8 + ADD $2, R7 // i++ + + CMPBLT R7, R12, A6 + BR E6 + +L6: MOVD (R8)(R1*1), R6 + MULHDU R9, R6 + MOVD (R2)(R1*1), R10 + ADDC R10, R11 //add to low order bits + ADDE R0, R6 + ADDC R4, R11 + ADDE R0, R6 + MOVD R6, R4 + MOVD R11, (R2)(R1*1) + + ADD $8, R1 // i*8 + 8 + ADD $1, R7 // i++ + +E6: CMPBLT R7, R5, L6 // i < n + + MOVD R4, c+56(FP) + RET + +// func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) +// CX = R4, r8 = r8, r9=r9, r10 = r2 , r11 = r5, AX = r11, DX = R6, r12=r12, BX = R1(*8) , (R0 set to 0) + use R11 + use R7 for i +TEXT ·divWVW(SB),NOSPLIT,$0 + MOVD z+0(FP), R2 + MOVD xn+24(FP), R10 // r = xn + MOVD x+32(FP), R8 + MOVD y+56(FP), R9 + MOVD z_len+8(FP), R7 // i = z + SLD $3, R7, R1 // i*8 + MOVD $0, R0 // make sure it's zero + BR E7 + +L7: MOVD (R8)(R1*1), R11 + WORD $0xB98700A9 //DLGR R10,R9 + MOVD R11, (R2)(R1*1) + +E7: SUB $1, R7 // i-- + SUB $8, R1 + BGE L7 // i >= 0 + + MOVD R10, r+64(FP) + RET diff --git a/vendor/github.com/golang/go/src/math/big/decimal.go b/vendor/github.com/golang/go/src/math/big/decimal.go new file mode 100644 index 000000000000..ae9ffb5db6ab --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/decimal.go @@ -0,0 +1,267 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements multi-precision decimal numbers. +// The implementation is for float to decimal conversion only; +// not general purpose use. +// The only operations are precise conversion from binary to +// decimal and rounding. +// +// The key observation and some code (shr) is borrowed from +// strconv/decimal.go: conversion of binary fractional values can be done +// precisely in multi-precision decimal because 2 divides 10 (required for +// >> of mantissa); but conversion of decimal floating-point values cannot +// be done precisely in binary representation. +// +// In contrast to strconv/decimal.go, only right shift is implemented in +// decimal format - left shift can be done precisely in binary format. + +package big + +// A decimal represents an unsigned floating-point number in decimal representation. +// The value of a non-zero decimal d is d.mant * 10**d.exp with 0.1 <= d.mant < 1, +// with the most-significant mantissa digit at index 0. For the zero decimal, the +// mantissa length and exponent are 0. +// The zero value for decimal represents a ready-to-use 0.0. +type decimal struct { + mant []byte // mantissa ASCII digits, big-endian + exp int // exponent +} + +// at returns the i'th mantissa digit, starting with the most significant digit at 0. +func (d *decimal) at(i int) byte { + if 0 <= i && i < len(d.mant) { + return d.mant[i] + } + return '0' +} + +// Maximum shift amount that can be done in one pass without overflow. +// A Word has _W bits and (1<= 0), or m >> -shift (for shift < 0). +func (x *decimal) init(m nat, shift int) { + // special case 0 + if len(m) == 0 { + x.mant = x.mant[:0] + x.exp = 0 + return + } + + // Optimization: If we need to shift right, first remove any trailing + // zero bits from m to reduce shift amount that needs to be done in + // decimal format (since that is likely slower). + if shift < 0 { + ntz := m.trailingZeroBits() + s := uint(-shift) + if s >= ntz { + s = ntz // shift at most ntz bits + } + m = nat(nil).shr(m, s) + shift += int(s) + } + + // Do any shift left in binary representation. + if shift > 0 { + m = nat(nil).shl(m, uint(shift)) + shift = 0 + } + + // Convert mantissa into decimal representation. + s := m.utoa(10) + n := len(s) + x.exp = n + // Trim trailing zeros; instead the exponent is tracking + // the decimal point independent of the number of digits. + for n > 0 && s[n-1] == '0' { + n-- + } + x.mant = append(x.mant[:0], s[:n]...) + + // Do any (remaining) shift right in decimal representation. + if shift < 0 { + for shift < -maxShift { + shr(x, maxShift) + shift += maxShift + } + shr(x, uint(-shift)) + } +} + +// shr implements x >> s, for s <= maxShift. +func shr(x *decimal, s uint) { + // Division by 1<>s == 0 && r < len(x.mant) { + ch := Word(x.mant[r]) + r++ + n = n*10 + ch - '0' + } + if n == 0 { + // x == 0; shouldn't get here, but handle anyway + x.mant = x.mant[:0] + return + } + for n>>s == 0 { + r++ + n *= 10 + } + x.exp += 1 - r + + // read a digit, write a digit + w := 0 // write index + mask := Word(1)<> s + n &= mask // n -= d << s + x.mant[w] = byte(d + '0') + w++ + n = n*10 + ch - '0' + } + + // write extra digits that still fit + for n > 0 && w < len(x.mant) { + d := n >> s + n &= mask + x.mant[w] = byte(d + '0') + w++ + n = n * 10 + } + x.mant = x.mant[:w] // the number may be shorter (e.g. 1024 >> 10) + + // append additional digits that didn't fit + for n > 0 { + d := n >> s + n &= mask + x.mant = append(x.mant, byte(d+'0')) + n = n * 10 + } + + trim(x) +} + +func (x *decimal) String() string { + if len(x.mant) == 0 { + return "0" + } + + var buf []byte + switch { + case x.exp <= 0: + // 0.00ddd + buf = append(buf, "0."...) + buf = appendZeros(buf, -x.exp) + buf = append(buf, x.mant...) + + case /* 0 < */ x.exp < len(x.mant): + // dd.ddd + buf = append(buf, x.mant[:x.exp]...) + buf = append(buf, '.') + buf = append(buf, x.mant[x.exp:]...) + + default: // len(x.mant) <= x.exp + // ddd00 + buf = append(buf, x.mant...) + buf = appendZeros(buf, x.exp-len(x.mant)) + } + + return string(buf) +} + +// appendZeros appends n 0 digits to buf and returns buf. +func appendZeros(buf []byte, n int) []byte { + for ; n > 0; n-- { + buf = append(buf, '0') + } + return buf +} + +// shouldRoundUp reports if x should be rounded up +// if shortened to n digits. n must be a valid index +// for x.mant. +func shouldRoundUp(x *decimal, n int) bool { + if x.mant[n] == '5' && n+1 == len(x.mant) { + // exactly halfway - round to even + return n > 0 && (x.mant[n-1]-'0')&1 != 0 + } + // not halfway - digit tells all (x.mant has no trailing zeros) + return x.mant[n] >= '5' +} + +// round sets x to (at most) n mantissa digits by rounding it +// to the nearest even value with n (or fever) mantissa digits. +// If n < 0, x remains unchanged. +func (x *decimal) round(n int) { + if n < 0 || n >= len(x.mant) { + return // nothing to do + } + + if shouldRoundUp(x, n) { + x.roundUp(n) + } else { + x.roundDown(n) + } +} + +func (x *decimal) roundUp(n int) { + if n < 0 || n >= len(x.mant) { + return // nothing to do + } + // 0 <= n < len(x.mant) + + // find first digit < '9' + for n > 0 && x.mant[n-1] >= '9' { + n-- + } + + if n == 0 { + // all digits are '9's => round up to '1' and update exponent + x.mant[0] = '1' // ok since len(x.mant) > n + x.mant = x.mant[:1] + x.exp++ + return + } + + // n > 0 && x.mant[n-1] < '9' + x.mant[n-1]++ + x.mant = x.mant[:n] + // x already trimmed +} + +func (x *decimal) roundDown(n int) { + if n < 0 || n >= len(x.mant) { + return // nothing to do + } + x.mant = x.mant[:n] + trim(x) +} + +// trim cuts off any trailing zeros from x's mantissa; +// they are meaningless for the value of x. +func trim(x *decimal) { + i := len(x.mant) + for i > 0 && x.mant[i-1] == '0' { + i-- + } + x.mant = x.mant[:i] + if i == 0 { + x.exp = 0 + } +} diff --git a/vendor/github.com/golang/go/src/math/big/doc.go b/vendor/github.com/golang/go/src/math/big/doc.go new file mode 100644 index 000000000000..65ed019b741d --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/doc.go @@ -0,0 +1,99 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package big implements arbitrary-precision arithmetic (big numbers). +The following numeric types are supported: + + Int signed integers + Rat rational numbers + Float floating-point numbers + +The zero value for an Int, Rat, or Float correspond to 0. Thus, new +values can be declared in the usual ways and denote 0 without further +initialization: + + var x Int // &x is an *Int of value 0 + var r = &Rat{} // r is a *Rat of value 0 + y := new(Float) // y is a *Float of value 0 + +Alternatively, new values can be allocated and initialized with factory +functions of the form: + + func NewT(v V) *T + +For instance, NewInt(x) returns an *Int set to the value of the int64 +argument x, NewRat(a, b) returns a *Rat set to the fraction a/b where +a and b are int64 values, and NewFloat(f) returns a *Float initialized +to the float64 argument f. More flexibility is provided with explicit +setters, for instance: + + var z1 Int + z1.SetUint64(123) // z1 := 123 + z2 := new(Rat).SetFloat64(1.25) // z2 := 5/4 + z3 := new(Float).SetInt(z1) // z3 := 123.0 + +Setters, numeric operations and predicates are represented as methods of +the form: + + func (z *T) SetV(v V) *T // z = v + func (z *T) Unary(x *T) *T // z = unary x + func (z *T) Binary(x, y *T) *T // z = x binary y + func (x *T) Pred() P // p = pred(x) + +with T one of Int, Rat, or Float. For unary and binary operations, the +result is the receiver (usually named z in that case; see below); if it +is one of the operands x or y it may be safely overwritten (and its memory +reused). + +Arithmetic expressions are typically written as a sequence of individual +method calls, with each call corresponding to an operation. The receiver +denotes the result and the method arguments are the operation's operands. +For instance, given three *Int values a, b and c, the invocation + + c.Add(a, b) + +computes the sum a + b and stores the result in c, overwriting whatever +value was held in c before. Unless specified otherwise, operations permit +aliasing of parameters, so it is perfectly ok to write + + sum.Add(sum, x) + +to accumulate values x in a sum. + +(By always passing in a result value via the receiver, memory use can be +much better controlled. Instead of having to allocate new memory for each +result, an operation can reuse the space allocated for the result value, +and overwrite that value with the new result in the process.) + +Notational convention: Incoming method parameters (including the receiver) +are named consistently in the API to clarify their use. Incoming operands +are usually named x, y, a, b, and so on, but never z. A parameter specifying +the result is named z (typically the receiver). + +For instance, the arguments for (*Int).Add are named x and y, and because +the receiver specifies the result destination, it is called z: + + func (z *Int) Add(x, y *Int) *Int + +Methods of this form typically return the incoming receiver as well, to +enable simple call chaining. + +Methods which don't require a result value to be passed in (for instance, +Int.Sign), simply return the result. In this case, the receiver is typically +the first operand, named x: + + func (x *Int) Sign() int + +Various methods support conversions between strings and corresponding +numeric values, and vice versa: *Int, *Rat, and *Float values implement +the Stringer interface for a (default) string representation of the value, +but also provide SetString methods to initialize a value from a string in +a variety of supported formats (see the respective SetString documentation). + +Finally, *Int, *Rat, and *Float satisfy the fmt package's Scanner interface +for scanning and (except for *Rat) the Formatter interface for formatted +printing. +*/ +package big diff --git a/vendor/github.com/golang/go/src/math/big/float.go b/vendor/github.com/golang/go/src/math/big/float.go new file mode 100644 index 000000000000..c042854ebaaf --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/float.go @@ -0,0 +1,1717 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements multi-precision floating-point numbers. +// Like in the GNU MPFR library (http://www.mpfr.org/), operands +// can be of mixed precision. Unlike MPFR, the rounding mode is +// not specified with each operation, but with each operand. The +// rounding mode of the result operand determines the rounding +// mode of an operation. This is a from-scratch implementation. + +package big + +import ( + "fmt" + "math" + "math/bits" +) + +const debugFloat = false // enable for debugging + +// A nonzero finite Float represents a multi-precision floating point number +// +// sign × mantissa × 2**exponent +// +// with 0.5 <= mantissa < 1.0, and MinExp <= exponent <= MaxExp. +// A Float may also be zero (+0, -0) or infinite (+Inf, -Inf). +// All Floats are ordered, and the ordering of two Floats x and y +// is defined by x.Cmp(y). +// +// Each Float value also has a precision, rounding mode, and accuracy. +// The precision is the maximum number of mantissa bits available to +// represent the value. The rounding mode specifies how a result should +// be rounded to fit into the mantissa bits, and accuracy describes the +// rounding error with respect to the exact result. +// +// Unless specified otherwise, all operations (including setters) that +// specify a *Float variable for the result (usually via the receiver +// with the exception of MantExp), round the numeric result according +// to the precision and rounding mode of the result variable. +// +// If the provided result precision is 0 (see below), it is set to the +// precision of the argument with the largest precision value before any +// rounding takes place, and the rounding mode remains unchanged. Thus, +// uninitialized Floats provided as result arguments will have their +// precision set to a reasonable value determined by the operands and +// their mode is the zero value for RoundingMode (ToNearestEven). +// +// By setting the desired precision to 24 or 53 and using matching rounding +// mode (typically ToNearestEven), Float operations produce the same results +// as the corresponding float32 or float64 IEEE-754 arithmetic for operands +// that correspond to normal (i.e., not denormal) float32 or float64 numbers. +// Exponent underflow and overflow lead to a 0 or an Infinity for different +// values than IEEE-754 because Float exponents have a much larger range. +// +// The zero (uninitialized) value for a Float is ready to use and represents +// the number +0.0 exactly, with precision 0 and rounding mode ToNearestEven. +// +type Float struct { + prec uint32 + mode RoundingMode + acc Accuracy + form form + neg bool + mant nat + exp int32 +} + +// An ErrNaN panic is raised by a Float operation that would lead to +// a NaN under IEEE-754 rules. An ErrNaN implements the error interface. +type ErrNaN struct { + msg string +} + +func (err ErrNaN) Error() string { + return err.msg +} + +// NewFloat allocates and returns a new Float set to x, +// with precision 53 and rounding mode ToNearestEven. +// NewFloat panics with ErrNaN if x is a NaN. +func NewFloat(x float64) *Float { + if math.IsNaN(x) { + panic(ErrNaN{"NewFloat(NaN)"}) + } + return new(Float).SetFloat64(x) +} + +// Exponent and precision limits. +const ( + MaxExp = math.MaxInt32 // largest supported exponent + MinExp = math.MinInt32 // smallest supported exponent + MaxPrec = math.MaxUint32 // largest (theoretically) supported precision; likely memory-limited +) + +// Internal representation: The mantissa bits x.mant of a nonzero finite +// Float x are stored in a nat slice long enough to hold up to x.prec bits; +// the slice may (but doesn't have to) be shorter if the mantissa contains +// trailing 0 bits. x.mant is normalized if the msb of x.mant == 1 (i.e., +// the msb is shifted all the way "to the left"). Thus, if the mantissa has +// trailing 0 bits or x.prec is not a multiple of the Word size _W, +// x.mant[0] has trailing zero bits. The msb of the mantissa corresponds +// to the value 0.5; the exponent x.exp shifts the binary point as needed. +// +// A zero or non-finite Float x ignores x.mant and x.exp. +// +// x form neg mant exp +// ---------------------------------------------------------- +// ±0 zero sign - - +// 0 < |x| < +Inf finite sign mantissa exponent +// ±Inf inf sign - - + +// A form value describes the internal representation. +type form byte + +// The form value order is relevant - do not change! +const ( + zero form = iota + finite + inf +) + +// RoundingMode determines how a Float value is rounded to the +// desired precision. Rounding may change the Float value; the +// rounding error is described by the Float's Accuracy. +type RoundingMode byte + +// These constants define supported rounding modes. +const ( + ToNearestEven RoundingMode = iota // == IEEE 754-2008 roundTiesToEven + ToNearestAway // == IEEE 754-2008 roundTiesToAway + ToZero // == IEEE 754-2008 roundTowardZero + AwayFromZero // no IEEE 754-2008 equivalent + ToNegativeInf // == IEEE 754-2008 roundTowardNegative + ToPositiveInf // == IEEE 754-2008 roundTowardPositive +) + +//go:generate stringer -type=RoundingMode + +// Accuracy describes the rounding error produced by the most recent +// operation that generated a Float value, relative to the exact value. +type Accuracy int8 + +// Constants describing the Accuracy of a Float. +const ( + Below Accuracy = -1 + Exact Accuracy = 0 + Above Accuracy = +1 +) + +//go:generate stringer -type=Accuracy + +// SetPrec sets z's precision to prec and returns the (possibly) rounded +// value of z. Rounding occurs according to z's rounding mode if the mantissa +// cannot be represented in prec bits without loss of precision. +// SetPrec(0) maps all finite values to ±0; infinite values remain unchanged. +// If prec > MaxPrec, it is set to MaxPrec. +func (z *Float) SetPrec(prec uint) *Float { + z.acc = Exact // optimistically assume no rounding is needed + + // special case + if prec == 0 { + z.prec = 0 + if z.form == finite { + // truncate z to 0 + z.acc = makeAcc(z.neg) + z.form = zero + } + return z + } + + // general case + if prec > MaxPrec { + prec = MaxPrec + } + old := z.prec + z.prec = uint32(prec) + if z.prec < old { + z.round(0) + } + return z +} + +func makeAcc(above bool) Accuracy { + if above { + return Above + } + return Below +} + +// SetMode sets z's rounding mode to mode and returns an exact z. +// z remains unchanged otherwise. +// z.SetMode(z.Mode()) is a cheap way to set z's accuracy to Exact. +func (z *Float) SetMode(mode RoundingMode) *Float { + z.mode = mode + z.acc = Exact + return z +} + +// Prec returns the mantissa precision of x in bits. +// The result may be 0 for |x| == 0 and |x| == Inf. +func (x *Float) Prec() uint { + return uint(x.prec) +} + +// MinPrec returns the minimum precision required to represent x exactly +// (i.e., the smallest prec before x.SetPrec(prec) would start rounding x). +// The result is 0 for |x| == 0 and |x| == Inf. +func (x *Float) MinPrec() uint { + if x.form != finite { + return 0 + } + return uint(len(x.mant))*_W - x.mant.trailingZeroBits() +} + +// Mode returns the rounding mode of x. +func (x *Float) Mode() RoundingMode { + return x.mode +} + +// Acc returns the accuracy of x produced by the most recent operation. +func (x *Float) Acc() Accuracy { + return x.acc +} + +// Sign returns: +// +// -1 if x < 0 +// 0 if x is ±0 +// +1 if x > 0 +// +func (x *Float) Sign() int { + if debugFloat { + x.validate() + } + if x.form == zero { + return 0 + } + if x.neg { + return -1 + } + return 1 +} + +// MantExp breaks x into its mantissa and exponent components +// and returns the exponent. If a non-nil mant argument is +// provided its value is set to the mantissa of x, with the +// same precision and rounding mode as x. The components +// satisfy x == mant × 2**exp, with 0.5 <= |mant| < 1.0. +// Calling MantExp with a nil argument is an efficient way to +// get the exponent of the receiver. +// +// Special cases are: +// +// ( ±0).MantExp(mant) = 0, with mant set to ±0 +// (±Inf).MantExp(mant) = 0, with mant set to ±Inf +// +// x and mant may be the same in which case x is set to its +// mantissa value. +func (x *Float) MantExp(mant *Float) (exp int) { + if debugFloat { + x.validate() + } + if x.form == finite { + exp = int(x.exp) + } + if mant != nil { + mant.Copy(x) + if mant.form == finite { + mant.exp = 0 + } + } + return +} + +func (z *Float) setExpAndRound(exp int64, sbit uint) { + if exp < MinExp { + // underflow + z.acc = makeAcc(z.neg) + z.form = zero + return + } + + if exp > MaxExp { + // overflow + z.acc = makeAcc(!z.neg) + z.form = inf + return + } + + z.form = finite + z.exp = int32(exp) + z.round(sbit) +} + +// SetMantExp sets z to mant × 2**exp and and returns z. +// The result z has the same precision and rounding mode +// as mant. SetMantExp is an inverse of MantExp but does +// not require 0.5 <= |mant| < 1.0. Specifically: +// +// mant := new(Float) +// new(Float).SetMantExp(mant, x.MantExp(mant)).Cmp(x) == 0 +// +// Special cases are: +// +// z.SetMantExp( ±0, exp) = ±0 +// z.SetMantExp(±Inf, exp) = ±Inf +// +// z and mant may be the same in which case z's exponent +// is set to exp. +func (z *Float) SetMantExp(mant *Float, exp int) *Float { + if debugFloat { + z.validate() + mant.validate() + } + z.Copy(mant) + if z.form != finite { + return z + } + z.setExpAndRound(int64(z.exp)+int64(exp), 0) + return z +} + +// Signbit returns true if x is negative or negative zero. +func (x *Float) Signbit() bool { + return x.neg +} + +// IsInf reports whether x is +Inf or -Inf. +func (x *Float) IsInf() bool { + return x.form == inf +} + +// IsInt reports whether x is an integer. +// ±Inf values are not integers. +func (x *Float) IsInt() bool { + if debugFloat { + x.validate() + } + // special cases + if x.form != finite { + return x.form == zero + } + // x.form == finite + if x.exp <= 0 { + return false + } + // x.exp > 0 + return x.prec <= uint32(x.exp) || x.MinPrec() <= uint(x.exp) // not enough bits for fractional mantissa +} + +// debugging support +func (x *Float) validate() { + if !debugFloat { + // avoid performance bugs + panic("validate called but debugFloat is not set") + } + if x.form != finite { + return + } + m := len(x.mant) + if m == 0 { + panic("nonzero finite number with empty mantissa") + } + const msb = 1 << (_W - 1) + if x.mant[m-1]&msb == 0 { + panic(fmt.Sprintf("msb not set in last word %#x of %s", x.mant[m-1], x.Text('p', 0))) + } + if x.prec == 0 { + panic("zero precision finite number") + } +} + +// round rounds z according to z.mode to z.prec bits and sets z.acc accordingly. +// sbit must be 0 or 1 and summarizes any "sticky bit" information one might +// have before calling round. z's mantissa must be normalized (with the msb set) +// or empty. +// +// CAUTION: The rounding modes ToNegativeInf, ToPositiveInf are affected by the +// sign of z. For correct rounding, the sign of z must be set correctly before +// calling round. +func (z *Float) round(sbit uint) { + if debugFloat { + z.validate() + } + + z.acc = Exact + if z.form != finite { + // ±0 or ±Inf => nothing left to do + return + } + // z.form == finite && len(z.mant) > 0 + // m > 0 implies z.prec > 0 (checked by validate) + + m := uint32(len(z.mant)) // present mantissa length in words + bits := m * _W // present mantissa bits; bits > 0 + if bits <= z.prec { + // mantissa fits => nothing to do + return + } + // bits > z.prec + + // Rounding is based on two bits: the rounding bit (rbit) and the + // sticky bit (sbit). The rbit is the bit immediately before the + // z.prec leading mantissa bits (the "0.5"). The sbit is set if any + // of the bits before the rbit are set (the "0.25", "0.125", etc.): + // + // rbit sbit => "fractional part" + // + // 0 0 == 0 + // 0 1 > 0 , < 0.5 + // 1 0 == 0.5 + // 1 1 > 0.5, < 1.0 + + // bits > z.prec: mantissa too large => round + r := uint(bits - z.prec - 1) // rounding bit position; r >= 0 + rbit := z.mant.bit(r) & 1 // rounding bit; be safe and ensure it's a single bit + // The sticky bit is only needed for rounding ToNearestEven + // or when the rounding bit is zero. Avoid computation otherwise. + if sbit == 0 && (rbit == 0 || z.mode == ToNearestEven) { + sbit = z.mant.sticky(r) + } + sbit &= 1 // be safe and ensure it's a single bit + + // cut off extra words + n := (z.prec + (_W - 1)) / _W // mantissa length in words for desired precision + if m > n { + copy(z.mant, z.mant[m-n:]) // move n last words to front + z.mant = z.mant[:n] + } + + // determine number of trailing zero bits (ntz) and compute lsb mask of mantissa's least-significant word + ntz := n*_W - z.prec // 0 <= ntz < _W + lsb := Word(1) << ntz + + // round if result is inexact + if rbit|sbit != 0 { + // Make rounding decision: The result mantissa is truncated ("rounded down") + // by default. Decide if we need to increment, or "round up", the (unsigned) + // mantissa. + inc := false + switch z.mode { + case ToNegativeInf: + inc = z.neg + case ToZero: + // nothing to do + case ToNearestEven: + inc = rbit != 0 && (sbit != 0 || z.mant[0]&lsb != 0) + case ToNearestAway: + inc = rbit != 0 + case AwayFromZero: + inc = true + case ToPositiveInf: + inc = !z.neg + default: + panic("unreachable") + } + + // A positive result (!z.neg) is Above the exact result if we increment, + // and it's Below if we truncate (Exact results require no rounding). + // For a negative result (z.neg) it is exactly the opposite. + z.acc = makeAcc(inc != z.neg) + + if inc { + // add 1 to mantissa + if addVW(z.mant, z.mant, lsb) != 0 { + // mantissa overflow => adjust exponent + if z.exp >= MaxExp { + // exponent overflow + z.form = inf + return + } + z.exp++ + // adjust mantissa: divide by 2 to compensate for exponent adjustment + shrVU(z.mant, z.mant, 1) + // set msb == carry == 1 from the mantissa overflow above + const msb = 1 << (_W - 1) + z.mant[n-1] |= msb + } + } + } + + // zero out trailing bits in least-significant word + z.mant[0] &^= lsb - 1 + + if debugFloat { + z.validate() + } +} + +func (z *Float) setBits64(neg bool, x uint64) *Float { + if z.prec == 0 { + z.prec = 64 + } + z.acc = Exact + z.neg = neg + if x == 0 { + z.form = zero + return z + } + // x != 0 + z.form = finite + s := bits.LeadingZeros64(x) + z.mant = z.mant.setUint64(x << uint(s)) + z.exp = int32(64 - s) // always fits + if z.prec < 64 { + z.round(0) + } + return z +} + +// SetUint64 sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to 64 (and rounding will have +// no effect). +func (z *Float) SetUint64(x uint64) *Float { + return z.setBits64(false, x) +} + +// SetInt64 sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to 64 (and rounding will have +// no effect). +func (z *Float) SetInt64(x int64) *Float { + u := x + if u < 0 { + u = -u + } + // We cannot simply call z.SetUint64(uint64(u)) and change + // the sign afterwards because the sign affects rounding. + return z.setBits64(x < 0, uint64(u)) +} + +// SetFloat64 sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to 53 (and rounding will have +// no effect). SetFloat64 panics with ErrNaN if x is a NaN. +func (z *Float) SetFloat64(x float64) *Float { + if z.prec == 0 { + z.prec = 53 + } + if math.IsNaN(x) { + panic(ErrNaN{"Float.SetFloat64(NaN)"}) + } + z.acc = Exact + z.neg = math.Signbit(x) // handle -0, -Inf correctly + if x == 0 { + z.form = zero + return z + } + if math.IsInf(x, 0) { + z.form = inf + return z + } + // normalized x != 0 + z.form = finite + fmant, exp := math.Frexp(x) // get normalized mantissa + z.mant = z.mant.setUint64(1<<63 | math.Float64bits(fmant)<<11) + z.exp = int32(exp) // always fits + if z.prec < 53 { + z.round(0) + } + return z +} + +// fnorm normalizes mantissa m by shifting it to the left +// such that the msb of the most-significant word (msw) is 1. +// It returns the shift amount. It assumes that len(m) != 0. +func fnorm(m nat) int64 { + if debugFloat && (len(m) == 0 || m[len(m)-1] == 0) { + panic("msw of mantissa is 0") + } + s := nlz(m[len(m)-1]) + if s > 0 { + c := shlVU(m, m, s) + if debugFloat && c != 0 { + panic("nlz or shlVU incorrect") + } + } + return int64(s) +} + +// SetInt sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to the larger of x.BitLen() +// or 64 (and rounding will have no effect). +func (z *Float) SetInt(x *Int) *Float { + // TODO(gri) can be more efficient if z.prec > 0 + // but small compared to the size of x, or if there + // are many trailing 0's. + bits := uint32(x.BitLen()) + if z.prec == 0 { + z.prec = umax32(bits, 64) + } + z.acc = Exact + z.neg = x.neg + if len(x.abs) == 0 { + z.form = zero + return z + } + // x != 0 + z.mant = z.mant.set(x.abs) + fnorm(z.mant) + z.setExpAndRound(int64(bits), 0) + return z +} + +// SetRat sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to the largest of a.BitLen(), +// b.BitLen(), or 64; with x = a/b. +func (z *Float) SetRat(x *Rat) *Float { + if x.IsInt() { + return z.SetInt(x.Num()) + } + var a, b Float + a.SetInt(x.Num()) + b.SetInt(x.Denom()) + if z.prec == 0 { + z.prec = umax32(a.prec, b.prec) + } + return z.Quo(&a, &b) +} + +// SetInf sets z to the infinite Float -Inf if signbit is +// set, or +Inf if signbit is not set, and returns z. The +// precision of z is unchanged and the result is always +// Exact. +func (z *Float) SetInf(signbit bool) *Float { + z.acc = Exact + z.form = inf + z.neg = signbit + return z +} + +// Set sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to the precision of x +// before setting z (and rounding will have no effect). +// Rounding is performed according to z's precision and rounding +// mode; and z's accuracy reports the result error relative to the +// exact (not rounded) result. +func (z *Float) Set(x *Float) *Float { + if debugFloat { + x.validate() + } + z.acc = Exact + if z != x { + z.form = x.form + z.neg = x.neg + if x.form == finite { + z.exp = x.exp + z.mant = z.mant.set(x.mant) + } + if z.prec == 0 { + z.prec = x.prec + } else if z.prec < x.prec { + z.round(0) + } + } + return z +} + +// Copy sets z to x, with the same precision, rounding mode, and +// accuracy as x, and returns z. x is not changed even if z and +// x are the same. +func (z *Float) Copy(x *Float) *Float { + if debugFloat { + x.validate() + } + if z != x { + z.prec = x.prec + z.mode = x.mode + z.acc = x.acc + z.form = x.form + z.neg = x.neg + if z.form == finite { + z.mant = z.mant.set(x.mant) + z.exp = x.exp + } + } + return z +} + +// msb32 returns the 32 most significant bits of x. +func msb32(x nat) uint32 { + i := len(x) - 1 + if i < 0 { + return 0 + } + if debugFloat && x[i]&(1<<(_W-1)) == 0 { + panic("x not normalized") + } + switch _W { + case 32: + return uint32(x[i]) + case 64: + return uint32(x[i] >> 32) + } + panic("unreachable") +} + +// msb64 returns the 64 most significant bits of x. +func msb64(x nat) uint64 { + i := len(x) - 1 + if i < 0 { + return 0 + } + if debugFloat && x[i]&(1<<(_W-1)) == 0 { + panic("x not normalized") + } + switch _W { + case 32: + v := uint64(x[i]) << 32 + if i > 0 { + v |= uint64(x[i-1]) + } + return v + case 64: + return uint64(x[i]) + } + panic("unreachable") +} + +// Uint64 returns the unsigned integer resulting from truncating x +// towards zero. If 0 <= x <= math.MaxUint64, the result is Exact +// if x is an integer and Below otherwise. +// The result is (0, Above) for x < 0, and (math.MaxUint64, Below) +// for x > math.MaxUint64. +func (x *Float) Uint64() (uint64, Accuracy) { + if debugFloat { + x.validate() + } + + switch x.form { + case finite: + if x.neg { + return 0, Above + } + // 0 < x < +Inf + if x.exp <= 0 { + // 0 < x < 1 + return 0, Below + } + // 1 <= x < Inf + if x.exp <= 64 { + // u = trunc(x) fits into a uint64 + u := msb64(x.mant) >> (64 - uint32(x.exp)) + if x.MinPrec() <= 64 { + return u, Exact + } + return u, Below // x truncated + } + // x too large + return math.MaxUint64, Below + + case zero: + return 0, Exact + + case inf: + if x.neg { + return 0, Above + } + return math.MaxUint64, Below + } + + panic("unreachable") +} + +// Int64 returns the integer resulting from truncating x towards zero. +// If math.MinInt64 <= x <= math.MaxInt64, the result is Exact if x is +// an integer, and Above (x < 0) or Below (x > 0) otherwise. +// The result is (math.MinInt64, Above) for x < math.MinInt64, +// and (math.MaxInt64, Below) for x > math.MaxInt64. +func (x *Float) Int64() (int64, Accuracy) { + if debugFloat { + x.validate() + } + + switch x.form { + case finite: + // 0 < |x| < +Inf + acc := makeAcc(x.neg) + if x.exp <= 0 { + // 0 < |x| < 1 + return 0, acc + } + // x.exp > 0 + + // 1 <= |x| < +Inf + if x.exp <= 63 { + // i = trunc(x) fits into an int64 (excluding math.MinInt64) + i := int64(msb64(x.mant) >> (64 - uint32(x.exp))) + if x.neg { + i = -i + } + if x.MinPrec() <= uint(x.exp) { + return i, Exact + } + return i, acc // x truncated + } + if x.neg { + // check for special case x == math.MinInt64 (i.e., x == -(0.5 << 64)) + if x.exp == 64 && x.MinPrec() == 1 { + acc = Exact + } + return math.MinInt64, acc + } + // x too large + return math.MaxInt64, Below + + case zero: + return 0, Exact + + case inf: + if x.neg { + return math.MinInt64, Above + } + return math.MaxInt64, Below + } + + panic("unreachable") +} + +// Float32 returns the float32 value nearest to x. If x is too small to be +// represented by a float32 (|x| < math.SmallestNonzeroFloat32), the result +// is (0, Below) or (-0, Above), respectively, depending on the sign of x. +// If x is too large to be represented by a float32 (|x| > math.MaxFloat32), +// the result is (+Inf, Above) or (-Inf, Below), depending on the sign of x. +func (x *Float) Float32() (float32, Accuracy) { + if debugFloat { + x.validate() + } + + switch x.form { + case finite: + // 0 < |x| < +Inf + + const ( + fbits = 32 // float size + mbits = 23 // mantissa size (excluding implicit msb) + ebits = fbits - mbits - 1 // 8 exponent size + bias = 1<<(ebits-1) - 1 // 127 exponent bias + dmin = 1 - bias - mbits // -149 smallest unbiased exponent (denormal) + emin = 1 - bias // -126 smallest unbiased exponent (normal) + emax = bias // 127 largest unbiased exponent (normal) + ) + + // Float mantissa m is 0.5 <= m < 1.0; compute exponent e for float32 mantissa. + e := x.exp - 1 // exponent for normal mantissa m with 1.0 <= m < 2.0 + + // Compute precision p for float32 mantissa. + // If the exponent is too small, we have a denormal number before + // rounding and fewer than p mantissa bits of precision available + // (the exponent remains fixed but the mantissa gets shifted right). + p := mbits + 1 // precision of normal float + if e < emin { + // recompute precision + p = mbits + 1 - emin + int(e) + // If p == 0, the mantissa of x is shifted so much to the right + // that its msb falls immediately to the right of the float32 + // mantissa space. In other words, if the smallest denormal is + // considered "1.0", for p == 0, the mantissa value m is >= 0.5. + // If m > 0.5, it is rounded up to 1.0; i.e., the smallest denormal. + // If m == 0.5, it is rounded down to even, i.e., 0.0. + // If p < 0, the mantissa value m is <= "0.25" which is never rounded up. + if p < 0 /* m <= 0.25 */ || p == 0 && x.mant.sticky(uint(len(x.mant))*_W-1) == 0 /* m == 0.5 */ { + // underflow to ±0 + if x.neg { + var z float32 + return -z, Above + } + return 0.0, Below + } + // otherwise, round up + // We handle p == 0 explicitly because it's easy and because + // Float.round doesn't support rounding to 0 bits of precision. + if p == 0 { + if x.neg { + return -math.SmallestNonzeroFloat32, Below + } + return math.SmallestNonzeroFloat32, Above + } + } + // p > 0 + + // round + var r Float + r.prec = uint32(p) + r.Set(x) + e = r.exp - 1 + + // Rounding may have caused r to overflow to ±Inf + // (rounding never causes underflows to 0). + // If the exponent is too large, also overflow to ±Inf. + if r.form == inf || e > emax { + // overflow + if x.neg { + return float32(math.Inf(-1)), Below + } + return float32(math.Inf(+1)), Above + } + // e <= emax + + // Determine sign, biased exponent, and mantissa. + var sign, bexp, mant uint32 + if x.neg { + sign = 1 << (fbits - 1) + } + + // Rounding may have caused a denormal number to + // become normal. Check again. + if e < emin { + // denormal number: recompute precision + // Since rounding may have at best increased precision + // and we have eliminated p <= 0 early, we know p > 0. + // bexp == 0 for denormals + p = mbits + 1 - emin + int(e) + mant = msb32(r.mant) >> uint(fbits-p) + } else { + // normal number: emin <= e <= emax + bexp = uint32(e+bias) << mbits + mant = msb32(r.mant) >> ebits & (1< math.MaxFloat64), +// the result is (+Inf, Above) or (-Inf, Below), depending on the sign of x. +func (x *Float) Float64() (float64, Accuracy) { + if debugFloat { + x.validate() + } + + switch x.form { + case finite: + // 0 < |x| < +Inf + + const ( + fbits = 64 // float size + mbits = 52 // mantissa size (excluding implicit msb) + ebits = fbits - mbits - 1 // 11 exponent size + bias = 1<<(ebits-1) - 1 // 1023 exponent bias + dmin = 1 - bias - mbits // -1074 smallest unbiased exponent (denormal) + emin = 1 - bias // -1022 smallest unbiased exponent (normal) + emax = bias // 1023 largest unbiased exponent (normal) + ) + + // Float mantissa m is 0.5 <= m < 1.0; compute exponent e for float64 mantissa. + e := x.exp - 1 // exponent for normal mantissa m with 1.0 <= m < 2.0 + + // Compute precision p for float64 mantissa. + // If the exponent is too small, we have a denormal number before + // rounding and fewer than p mantissa bits of precision available + // (the exponent remains fixed but the mantissa gets shifted right). + p := mbits + 1 // precision of normal float + if e < emin { + // recompute precision + p = mbits + 1 - emin + int(e) + // If p == 0, the mantissa of x is shifted so much to the right + // that its msb falls immediately to the right of the float64 + // mantissa space. In other words, if the smallest denormal is + // considered "1.0", for p == 0, the mantissa value m is >= 0.5. + // If m > 0.5, it is rounded up to 1.0; i.e., the smallest denormal. + // If m == 0.5, it is rounded down to even, i.e., 0.0. + // If p < 0, the mantissa value m is <= "0.25" which is never rounded up. + if p < 0 /* m <= 0.25 */ || p == 0 && x.mant.sticky(uint(len(x.mant))*_W-1) == 0 /* m == 0.5 */ { + // underflow to ±0 + if x.neg { + var z float64 + return -z, Above + } + return 0.0, Below + } + // otherwise, round up + // We handle p == 0 explicitly because it's easy and because + // Float.round doesn't support rounding to 0 bits of precision. + if p == 0 { + if x.neg { + return -math.SmallestNonzeroFloat64, Below + } + return math.SmallestNonzeroFloat64, Above + } + } + // p > 0 + + // round + var r Float + r.prec = uint32(p) + r.Set(x) + e = r.exp - 1 + + // Rounding may have caused r to overflow to ±Inf + // (rounding never causes underflows to 0). + // If the exponent is too large, also overflow to ±Inf. + if r.form == inf || e > emax { + // overflow + if x.neg { + return math.Inf(-1), Below + } + return math.Inf(+1), Above + } + // e <= emax + + // Determine sign, biased exponent, and mantissa. + var sign, bexp, mant uint64 + if x.neg { + sign = 1 << (fbits - 1) + } + + // Rounding may have caused a denormal number to + // become normal. Check again. + if e < emin { + // denormal number: recompute precision + // Since rounding may have at best increased precision + // and we have eliminated p <= 0 early, we know p > 0. + // bexp == 0 for denormals + p = mbits + 1 - emin + int(e) + mant = msb64(r.mant) >> uint(fbits-p) + } else { + // normal number: emin <= e <= emax + bexp = uint64(e+bias) << mbits + mant = msb64(r.mant) >> ebits & (1< 0, and Above for x < 0. +// If a non-nil *Int argument z is provided, Int stores +// the result in z instead of allocating a new Int. +func (x *Float) Int(z *Int) (*Int, Accuracy) { + if debugFloat { + x.validate() + } + + if z == nil && x.form <= finite { + z = new(Int) + } + + switch x.form { + case finite: + // 0 < |x| < +Inf + acc := makeAcc(x.neg) + if x.exp <= 0 { + // 0 < |x| < 1 + return z.SetInt64(0), acc + } + // x.exp > 0 + + // 1 <= |x| < +Inf + // determine minimum required precision for x + allBits := uint(len(x.mant)) * _W + exp := uint(x.exp) + if x.MinPrec() <= exp { + acc = Exact + } + // shift mantissa as needed + if z == nil { + z = new(Int) + } + z.neg = x.neg + switch { + case exp > allBits: + z.abs = z.abs.shl(x.mant, exp-allBits) + default: + z.abs = z.abs.set(x.mant) + case exp < allBits: + z.abs = z.abs.shr(x.mant, allBits-exp) + } + return z, acc + + case zero: + return z.SetInt64(0), Exact + + case inf: + return nil, makeAcc(x.neg) + } + + panic("unreachable") +} + +// Rat returns the rational number corresponding to x; +// or nil if x is an infinity. +// The result is Exact if x is not an Inf. +// If a non-nil *Rat argument z is provided, Rat stores +// the result in z instead of allocating a new Rat. +func (x *Float) Rat(z *Rat) (*Rat, Accuracy) { + if debugFloat { + x.validate() + } + + if z == nil && x.form <= finite { + z = new(Rat) + } + + switch x.form { + case finite: + // 0 < |x| < +Inf + allBits := int32(len(x.mant)) * _W + // build up numerator and denominator + z.a.neg = x.neg + switch { + case x.exp > allBits: + z.a.abs = z.a.abs.shl(x.mant, uint(x.exp-allBits)) + z.b.abs = z.b.abs[:0] // == 1 (see Rat) + // z already in normal form + default: + z.a.abs = z.a.abs.set(x.mant) + z.b.abs = z.b.abs[:0] // == 1 (see Rat) + // z already in normal form + case x.exp < allBits: + z.a.abs = z.a.abs.set(x.mant) + t := z.b.abs.setUint64(1) + z.b.abs = t.shl(t, uint(allBits-x.exp)) + z.norm() + } + return z, Exact + + case zero: + return z.SetInt64(0), Exact + + case inf: + return nil, makeAcc(x.neg) + } + + panic("unreachable") +} + +// Abs sets z to the (possibly rounded) value |x| (the absolute value of x) +// and returns z. +func (z *Float) Abs(x *Float) *Float { + z.Set(x) + z.neg = false + return z +} + +// Neg sets z to the (possibly rounded) value of x with its sign negated, +// and returns z. +func (z *Float) Neg(x *Float) *Float { + z.Set(x) + z.neg = !z.neg + return z +} + +func validateBinaryOperands(x, y *Float) { + if !debugFloat { + // avoid performance bugs + panic("validateBinaryOperands called but debugFloat is not set") + } + if len(x.mant) == 0 { + panic("empty mantissa for x") + } + if len(y.mant) == 0 { + panic("empty mantissa for y") + } +} + +// z = x + y, ignoring signs of x and y for the addition +// but using the sign of z for rounding the result. +// x and y must have a non-empty mantissa and valid exponent. +func (z *Float) uadd(x, y *Float) { + // Note: This implementation requires 2 shifts most of the + // time. It is also inefficient if exponents or precisions + // differ by wide margins. The following article describes + // an efficient (but much more complicated) implementation + // compatible with the internal representation used here: + // + // Vincent Lefèvre: "The Generic Multiple-Precision Floating- + // Point Addition With Exact Rounding (as in the MPFR Library)" + // http://www.vinc17.net/research/papers/rnc6.pdf + + if debugFloat { + validateBinaryOperands(x, y) + } + + // compute exponents ex, ey for mantissa with "binary point" + // on the right (mantissa.0) - use int64 to avoid overflow + ex := int64(x.exp) - int64(len(x.mant))*_W + ey := int64(y.exp) - int64(len(y.mant))*_W + + al := alias(z.mant, x.mant) || alias(z.mant, y.mant) + + // TODO(gri) having a combined add-and-shift primitive + // could make this code significantly faster + switch { + case ex < ey: + if al { + t := nat(nil).shl(y.mant, uint(ey-ex)) + z.mant = z.mant.add(x.mant, t) + } else { + z.mant = z.mant.shl(y.mant, uint(ey-ex)) + z.mant = z.mant.add(x.mant, z.mant) + } + default: + // ex == ey, no shift needed + z.mant = z.mant.add(x.mant, y.mant) + case ex > ey: + if al { + t := nat(nil).shl(x.mant, uint(ex-ey)) + z.mant = z.mant.add(t, y.mant) + } else { + z.mant = z.mant.shl(x.mant, uint(ex-ey)) + z.mant = z.mant.add(z.mant, y.mant) + } + ex = ey + } + // len(z.mant) > 0 + + z.setExpAndRound(ex+int64(len(z.mant))*_W-fnorm(z.mant), 0) +} + +// z = x - y for |x| > |y|, ignoring signs of x and y for the subtraction +// but using the sign of z for rounding the result. +// x and y must have a non-empty mantissa and valid exponent. +func (z *Float) usub(x, y *Float) { + // This code is symmetric to uadd. + // We have not factored the common code out because + // eventually uadd (and usub) should be optimized + // by special-casing, and the code will diverge. + + if debugFloat { + validateBinaryOperands(x, y) + } + + ex := int64(x.exp) - int64(len(x.mant))*_W + ey := int64(y.exp) - int64(len(y.mant))*_W + + al := alias(z.mant, x.mant) || alias(z.mant, y.mant) + + switch { + case ex < ey: + if al { + t := nat(nil).shl(y.mant, uint(ey-ex)) + z.mant = t.sub(x.mant, t) + } else { + z.mant = z.mant.shl(y.mant, uint(ey-ex)) + z.mant = z.mant.sub(x.mant, z.mant) + } + default: + // ex == ey, no shift needed + z.mant = z.mant.sub(x.mant, y.mant) + case ex > ey: + if al { + t := nat(nil).shl(x.mant, uint(ex-ey)) + z.mant = t.sub(t, y.mant) + } else { + z.mant = z.mant.shl(x.mant, uint(ex-ey)) + z.mant = z.mant.sub(z.mant, y.mant) + } + ex = ey + } + + // operands may have canceled each other out + if len(z.mant) == 0 { + z.acc = Exact + z.form = zero + z.neg = false + return + } + // len(z.mant) > 0 + + z.setExpAndRound(ex+int64(len(z.mant))*_W-fnorm(z.mant), 0) +} + +// z = x * y, ignoring signs of x and y for the multiplication +// but using the sign of z for rounding the result. +// x and y must have a non-empty mantissa and valid exponent. +func (z *Float) umul(x, y *Float) { + if debugFloat { + validateBinaryOperands(x, y) + } + + // Note: This is doing too much work if the precision + // of z is less than the sum of the precisions of x + // and y which is often the case (e.g., if all floats + // have the same precision). + // TODO(gri) Optimize this for the common case. + + e := int64(x.exp) + int64(y.exp) + if x == y { + z.mant = z.mant.sqr(x.mant) + } else { + z.mant = z.mant.mul(x.mant, y.mant) + } + z.setExpAndRound(e-fnorm(z.mant), 0) +} + +// z = x / y, ignoring signs of x and y for the division +// but using the sign of z for rounding the result. +// x and y must have a non-empty mantissa and valid exponent. +func (z *Float) uquo(x, y *Float) { + if debugFloat { + validateBinaryOperands(x, y) + } + + // mantissa length in words for desired result precision + 1 + // (at least one extra bit so we get the rounding bit after + // the division) + n := int(z.prec/_W) + 1 + + // compute adjusted x.mant such that we get enough result precision + xadj := x.mant + if d := n - len(x.mant) + len(y.mant); d > 0 { + // d extra words needed => add d "0 digits" to x + xadj = make(nat, len(x.mant)+d) + copy(xadj[d:], x.mant) + } + // TODO(gri): If we have too many digits (d < 0), we should be able + // to shorten x for faster division. But we must be extra careful + // with rounding in that case. + + // Compute d before division since there may be aliasing of x.mant + // (via xadj) or y.mant with z.mant. + d := len(xadj) - len(y.mant) + + // divide + var r nat + z.mant, r = z.mant.div(nil, xadj, y.mant) + e := int64(x.exp) - int64(y.exp) - int64(d-len(z.mant))*_W + + // The result is long enough to include (at least) the rounding bit. + // If there's a non-zero remainder, the corresponding fractional part + // (if it were computed), would have a non-zero sticky bit (if it were + // zero, it couldn't have a non-zero remainder). + var sbit uint + if len(r) > 0 { + sbit = 1 + } + + z.setExpAndRound(e-fnorm(z.mant), sbit) +} + +// ucmp returns -1, 0, or +1, depending on whether +// |x| < |y|, |x| == |y|, or |x| > |y|. +// x and y must have a non-empty mantissa and valid exponent. +func (x *Float) ucmp(y *Float) int { + if debugFloat { + validateBinaryOperands(x, y) + } + + switch { + case x.exp < y.exp: + return -1 + case x.exp > y.exp: + return +1 + } + // x.exp == y.exp + + // compare mantissas + i := len(x.mant) + j := len(y.mant) + for i > 0 || j > 0 { + var xm, ym Word + if i > 0 { + i-- + xm = x.mant[i] + } + if j > 0 { + j-- + ym = y.mant[j] + } + switch { + case xm < ym: + return -1 + case xm > ym: + return +1 + } + } + + return 0 +} + +// Handling of sign bit as defined by IEEE 754-2008, section 6.3: +// +// When neither the inputs nor result are NaN, the sign of a product or +// quotient is the exclusive OR of the operands’ signs; the sign of a sum, +// or of a difference x−y regarded as a sum x+(−y), differs from at most +// one of the addends’ signs; and the sign of the result of conversions, +// the quantize operation, the roundToIntegral operations, and the +// roundToIntegralExact (see 5.3.1) is the sign of the first or only operand. +// These rules shall apply even when operands or results are zero or infinite. +// +// When the sum of two operands with opposite signs (or the difference of +// two operands with like signs) is exactly zero, the sign of that sum (or +// difference) shall be +0 in all rounding-direction attributes except +// roundTowardNegative; under that attribute, the sign of an exact zero +// sum (or difference) shall be −0. However, x+x = x−(−x) retains the same +// sign as x even when x is zero. +// +// See also: https://play.golang.org/p/RtH3UCt5IH + +// Add sets z to the rounded sum x+y and returns z. If z's precision is 0, +// it is changed to the larger of x's or y's precision before the operation. +// Rounding is performed according to z's precision and rounding mode; and +// z's accuracy reports the result error relative to the exact (not rounded) +// result. Add panics with ErrNaN if x and y are infinities with opposite +// signs. The value of z is undefined in that case. +// +// BUG(gri) When rounding ToNegativeInf, the sign of Float values rounded to 0 is incorrect. +func (z *Float) Add(x, y *Float) *Float { + if debugFloat { + x.validate() + y.validate() + } + + if z.prec == 0 { + z.prec = umax32(x.prec, y.prec) + } + + if x.form == finite && y.form == finite { + // x + y (common case) + + // Below we set z.neg = x.neg, and when z aliases y this will + // change the y operand's sign. This is fine, because if an + // operand aliases the receiver it'll be overwritten, but we still + // want the original x.neg and y.neg values when we evaluate + // x.neg != y.neg, so we need to save y.neg before setting z.neg. + yneg := y.neg + + z.neg = x.neg + if x.neg == yneg { + // x + y == x + y + // (-x) + (-y) == -(x + y) + z.uadd(x, y) + } else { + // x + (-y) == x - y == -(y - x) + // (-x) + y == y - x == -(x - y) + if x.ucmp(y) > 0 { + z.usub(x, y) + } else { + z.neg = !z.neg + z.usub(y, x) + } + } + return z + } + + if x.form == inf && y.form == inf && x.neg != y.neg { + // +Inf + -Inf + // -Inf + +Inf + // value of z is undefined but make sure it's valid + z.acc = Exact + z.form = zero + z.neg = false + panic(ErrNaN{"addition of infinities with opposite signs"}) + } + + if x.form == zero && y.form == zero { + // ±0 + ±0 + z.acc = Exact + z.form = zero + z.neg = x.neg && y.neg // -0 + -0 == -0 + return z + } + + if x.form == inf || y.form == zero { + // ±Inf + y + // x + ±0 + return z.Set(x) + } + + // ±0 + y + // x + ±Inf + return z.Set(y) +} + +// Sub sets z to the rounded difference x-y and returns z. +// Precision, rounding, and accuracy reporting are as for Add. +// Sub panics with ErrNaN if x and y are infinities with equal +// signs. The value of z is undefined in that case. +func (z *Float) Sub(x, y *Float) *Float { + if debugFloat { + x.validate() + y.validate() + } + + if z.prec == 0 { + z.prec = umax32(x.prec, y.prec) + } + + if x.form == finite && y.form == finite { + // x - y (common case) + yneg := y.neg + z.neg = x.neg + if x.neg != yneg { + // x - (-y) == x + y + // (-x) - y == -(x + y) + z.uadd(x, y) + } else { + // x - y == x - y == -(y - x) + // (-x) - (-y) == y - x == -(x - y) + if x.ucmp(y) > 0 { + z.usub(x, y) + } else { + z.neg = !z.neg + z.usub(y, x) + } + } + return z + } + + if x.form == inf && y.form == inf && x.neg == y.neg { + // +Inf - +Inf + // -Inf - -Inf + // value of z is undefined but make sure it's valid + z.acc = Exact + z.form = zero + z.neg = false + panic(ErrNaN{"subtraction of infinities with equal signs"}) + } + + if x.form == zero && y.form == zero { + // ±0 - ±0 + z.acc = Exact + z.form = zero + z.neg = x.neg && !y.neg // -0 - +0 == -0 + return z + } + + if x.form == inf || y.form == zero { + // ±Inf - y + // x - ±0 + return z.Set(x) + } + + // ±0 - y + // x - ±Inf + return z.Neg(y) +} + +// Mul sets z to the rounded product x*y and returns z. +// Precision, rounding, and accuracy reporting are as for Add. +// Mul panics with ErrNaN if one operand is zero and the other +// operand an infinity. The value of z is undefined in that case. +func (z *Float) Mul(x, y *Float) *Float { + if debugFloat { + x.validate() + y.validate() + } + + if z.prec == 0 { + z.prec = umax32(x.prec, y.prec) + } + + z.neg = x.neg != y.neg + + if x.form == finite && y.form == finite { + // x * y (common case) + z.umul(x, y) + return z + } + + z.acc = Exact + if x.form == zero && y.form == inf || x.form == inf && y.form == zero { + // ±0 * ±Inf + // ±Inf * ±0 + // value of z is undefined but make sure it's valid + z.form = zero + z.neg = false + panic(ErrNaN{"multiplication of zero with infinity"}) + } + + if x.form == inf || y.form == inf { + // ±Inf * y + // x * ±Inf + z.form = inf + return z + } + + // ±0 * y + // x * ±0 + z.form = zero + return z +} + +// Quo sets z to the rounded quotient x/y and returns z. +// Precision, rounding, and accuracy reporting are as for Add. +// Quo panics with ErrNaN if both operands are zero or infinities. +// The value of z is undefined in that case. +func (z *Float) Quo(x, y *Float) *Float { + if debugFloat { + x.validate() + y.validate() + } + + if z.prec == 0 { + z.prec = umax32(x.prec, y.prec) + } + + z.neg = x.neg != y.neg + + if x.form == finite && y.form == finite { + // x / y (common case) + z.uquo(x, y) + return z + } + + z.acc = Exact + if x.form == zero && y.form == zero || x.form == inf && y.form == inf { + // ±0 / ±0 + // ±Inf / ±Inf + // value of z is undefined but make sure it's valid + z.form = zero + z.neg = false + panic(ErrNaN{"division of zero by zero or infinity by infinity"}) + } + + if x.form == zero || y.form == inf { + // ±0 / y + // x / ±Inf + z.form = zero + return z + } + + // x / ±0 + // ±Inf / y + z.form = inf + return z +} + +// Cmp compares x and y and returns: +// +// -1 if x < y +// 0 if x == y (incl. -0 == 0, -Inf == -Inf, and +Inf == +Inf) +// +1 if x > y +// +func (x *Float) Cmp(y *Float) int { + if debugFloat { + x.validate() + y.validate() + } + + mx := x.ord() + my := y.ord() + switch { + case mx < my: + return -1 + case mx > my: + return +1 + } + // mx == my + + // only if |mx| == 1 we have to compare the mantissae + switch mx { + case -1: + return y.ucmp(x) + case +1: + return x.ucmp(y) + } + + return 0 +} + +// ord classifies x and returns: +// +// -2 if -Inf == x +// -1 if -Inf < x < 0 +// 0 if x == 0 (signed or unsigned) +// +1 if 0 < x < +Inf +// +2 if x == +Inf +// +func (x *Float) ord() int { + var m int + switch x.form { + case finite: + m = 1 + case zero: + return 0 + case inf: + m = 2 + } + if x.neg { + m = -m + } + return m +} + +func umax32(x, y uint32) uint32 { + if x > y { + return x + } + return y +} diff --git a/vendor/github.com/golang/go/src/math/big/floatconv.go b/vendor/github.com/golang/go/src/math/big/floatconv.go new file mode 100644 index 000000000000..95d1bf84e243 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/floatconv.go @@ -0,0 +1,293 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements string-to-Float conversion functions. + +package big + +import ( + "fmt" + "io" + "strings" +) + +var floatZero Float + +// SetString sets z to the value of s and returns z and a boolean indicating +// success. s must be a floating-point number of the same format as accepted +// by Parse, with base argument 0. The entire string (not just a prefix) must +// be valid for success. If the operation failed, the value of z is undefined +// but the returned value is nil. +func (z *Float) SetString(s string) (*Float, bool) { + if f, _, err := z.Parse(s, 0); err == nil { + return f, true + } + return nil, false +} + +// scan is like Parse but reads the longest possible prefix representing a valid +// floating point number from an io.ByteScanner rather than a string. It serves +// as the implementation of Parse. It does not recognize ±Inf and does not expect +// EOF at the end. +func (z *Float) scan(r io.ByteScanner, base int) (f *Float, b int, err error) { + prec := z.prec + if prec == 0 { + prec = 64 + } + + // A reasonable value in case of an error. + z.form = zero + + // sign + z.neg, err = scanSign(r) + if err != nil { + return + } + + // mantissa + var fcount int // fractional digit count; valid if <= 0 + z.mant, b, fcount, err = z.mant.scan(r, base, true) + if err != nil { + return + } + + // exponent + var exp int64 + var ebase int + exp, ebase, err = scanExponent(r, true) + if err != nil { + return + } + + // special-case 0 + if len(z.mant) == 0 { + z.prec = prec + z.acc = Exact + z.form = zero + f = z + return + } + // len(z.mant) > 0 + + // The mantissa may have a decimal point (fcount <= 0) and there + // may be a nonzero exponent exp. The decimal point amounts to a + // division by b**(-fcount). An exponent means multiplication by + // ebase**exp. Finally, mantissa normalization (shift left) requires + // a correcting multiplication by 2**(-shiftcount). Multiplications + // are commutative, so we can apply them in any order as long as there + // is no loss of precision. We only have powers of 2 and 10, and + // we split powers of 10 into the product of the same powers of + // 2 and 5. This reduces the size of the multiplication factor + // needed for base-10 exponents. + + // normalize mantissa and determine initial exponent contributions + exp2 := int64(len(z.mant))*_W - fnorm(z.mant) + exp5 := int64(0) + + // determine binary or decimal exponent contribution of decimal point + if fcount < 0 { + // The mantissa has a "decimal" point ddd.dddd; and + // -fcount is the number of digits to the right of '.'. + // Adjust relevant exponent accordingly. + d := int64(fcount) + switch b { + case 10: + exp5 = d + fallthrough // 10**e == 5**e * 2**e + case 2: + exp2 += d + case 16: + exp2 += d * 4 // hexadecimal digits are 4 bits each + default: + panic("unexpected mantissa base") + } + // fcount consumed - not needed anymore + } + + // take actual exponent into account + switch ebase { + case 10: + exp5 += exp + fallthrough + case 2: + exp2 += exp + default: + panic("unexpected exponent base") + } + // exp consumed - not needed anymore + + // apply 2**exp2 + if MinExp <= exp2 && exp2 <= MaxExp { + z.prec = prec + z.form = finite + z.exp = int32(exp2) + f = z + } else { + err = fmt.Errorf("exponent overflow") + return + } + + if exp5 == 0 { + // no decimal exponent contribution + z.round(0) + return + } + // exp5 != 0 + + // apply 5**exp5 + p := new(Float).SetPrec(z.Prec() + 64) // use more bits for p -- TODO(gri) what is the right number? + if exp5 < 0 { + z.Quo(z, p.pow5(uint64(-exp5))) + } else { + z.Mul(z, p.pow5(uint64(exp5))) + } + + return +} + +// These powers of 5 fit into a uint64. +// +// for p, q := uint64(0), uint64(1); p < q; p, q = q, q*5 { +// fmt.Println(q) +// } +// +var pow5tab = [...]uint64{ + 1, + 5, + 25, + 125, + 625, + 3125, + 15625, + 78125, + 390625, + 1953125, + 9765625, + 48828125, + 244140625, + 1220703125, + 6103515625, + 30517578125, + 152587890625, + 762939453125, + 3814697265625, + 19073486328125, + 95367431640625, + 476837158203125, + 2384185791015625, + 11920928955078125, + 59604644775390625, + 298023223876953125, + 1490116119384765625, + 7450580596923828125, +} + +// pow5 sets z to 5**n and returns z. +// n must not be negative. +func (z *Float) pow5(n uint64) *Float { + const m = uint64(len(pow5tab) - 1) + if n <= m { + return z.SetUint64(pow5tab[n]) + } + // n > m + + z.SetUint64(pow5tab[m]) + n -= m + + // use more bits for f than for z + // TODO(gri) what is the right number? + f := new(Float).SetPrec(z.Prec() + 64).SetUint64(5) + + for n > 0 { + if n&1 != 0 { + z.Mul(z, f) + } + f.Mul(f, f) + n >>= 1 + } + + return z +} + +// Parse parses s which must contain a text representation of a floating- +// point number with a mantissa in the given conversion base (the exponent +// is always a decimal number), or a string representing an infinite value. +// +// It sets z to the (possibly rounded) value of the corresponding floating- +// point value, and returns z, the actual base b, and an error err, if any. +// The entire string (not just a prefix) must be consumed for success. +// If z's precision is 0, it is changed to 64 before rounding takes effect. +// The number must be of the form: +// +// number = [ sign ] [ prefix ] mantissa [ exponent ] | infinity . +// sign = "+" | "-" . +// prefix = "0" ( "x" | "X" | "b" | "B" ) . +// mantissa = digits | digits "." [ digits ] | "." digits . +// exponent = ( "E" | "e" | "p" ) [ sign ] digits . +// digits = digit { digit } . +// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" . +// infinity = [ sign ] ( "inf" | "Inf" ) . +// +// The base argument must be 0, 2, 10, or 16. Providing an invalid base +// argument will lead to a run-time panic. +// +// For base 0, the number prefix determines the actual base: A prefix of +// "0x" or "0X" selects base 16, and a "0b" or "0B" prefix selects +// base 2; otherwise, the actual base is 10 and no prefix is accepted. +// The octal prefix "0" is not supported (a leading "0" is simply +// considered a "0"). +// +// A "p" exponent indicates a binary (rather then decimal) exponent; +// for instance "0x1.fffffffffffffp1023" (using base 0) represents the +// maximum float64 value. For hexadecimal mantissae, the exponent must +// be binary, if present (an "e" or "E" exponent indicator cannot be +// distinguished from a mantissa digit). +// +// The returned *Float f is nil and the value of z is valid but not +// defined if an error is reported. +// +func (z *Float) Parse(s string, base int) (f *Float, b int, err error) { + // scan doesn't handle ±Inf + if len(s) == 3 && (s == "Inf" || s == "inf") { + f = z.SetInf(false) + return + } + if len(s) == 4 && (s[0] == '+' || s[0] == '-') && (s[1:] == "Inf" || s[1:] == "inf") { + f = z.SetInf(s[0] == '-') + return + } + + r := strings.NewReader(s) + if f, b, err = z.scan(r, base); err != nil { + return + } + + // entire string must have been consumed + if ch, err2 := r.ReadByte(); err2 == nil { + err = fmt.Errorf("expected end of string, found %q", ch) + } else if err2 != io.EOF { + err = err2 + } + + return +} + +// ParseFloat is like f.Parse(s, base) with f set to the given precision +// and rounding mode. +func ParseFloat(s string, base int, prec uint, mode RoundingMode) (f *Float, b int, err error) { + return new(Float).SetPrec(prec).SetMode(mode).Parse(s, base) +} + +var _ fmt.Scanner = &floatZero // *Float must implement fmt.Scanner + +// Scan is a support routine for fmt.Scanner; it sets z to the value of +// the scanned number. It accepts formats whose verbs are supported by +// fmt.Scan for floating point values, which are: +// 'b' (binary), 'e', 'E', 'f', 'F', 'g' and 'G'. +// Scan doesn't handle ±Inf. +func (z *Float) Scan(s fmt.ScanState, ch rune) error { + s.SkipSpace() + _, _, err := z.scan(byteReader{s}, 0) + return err +} diff --git a/vendor/github.com/golang/go/src/math/big/floatmarsh.go b/vendor/github.com/golang/go/src/math/big/floatmarsh.go new file mode 100644 index 000000000000..d1c1dab06917 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/floatmarsh.go @@ -0,0 +1,120 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements encoding/decoding of Floats. + +package big + +import ( + "encoding/binary" + "fmt" +) + +// Gob codec version. Permits backward-compatible changes to the encoding. +const floatGobVersion byte = 1 + +// GobEncode implements the gob.GobEncoder interface. +// The Float value and all its attributes (precision, +// rounding mode, accuracy) are marshaled. +func (x *Float) GobEncode() ([]byte, error) { + if x == nil { + return nil, nil + } + + // determine max. space (bytes) required for encoding + sz := 1 + 1 + 4 // version + mode|acc|form|neg (3+2+2+1bit) + prec + n := 0 // number of mantissa words + if x.form == finite { + // add space for mantissa and exponent + n = int((x.prec + (_W - 1)) / _W) // required mantissa length in words for given precision + // actual mantissa slice could be shorter (trailing 0's) or longer (unused bits): + // - if shorter, only encode the words present + // - if longer, cut off unused words when encoding in bytes + // (in practice, this should never happen since rounding + // takes care of it, but be safe and do it always) + if len(x.mant) < n { + n = len(x.mant) + } + // len(x.mant) >= n + sz += 4 + n*_S // exp + mant + } + buf := make([]byte, sz) + + buf[0] = floatGobVersion + b := byte(x.mode&7)<<5 | byte((x.acc+1)&3)<<3 | byte(x.form&3)<<1 + if x.neg { + b |= 1 + } + buf[1] = b + binary.BigEndian.PutUint32(buf[2:], x.prec) + + if x.form == finite { + binary.BigEndian.PutUint32(buf[6:], uint32(x.exp)) + x.mant[len(x.mant)-n:].bytes(buf[10:]) // cut off unused trailing words + } + + return buf, nil +} + +// GobDecode implements the gob.GobDecoder interface. +// The result is rounded per the precision and rounding mode of +// z unless z's precision is 0, in which case z is set exactly +// to the decoded value. +func (z *Float) GobDecode(buf []byte) error { + if len(buf) == 0 { + // Other side sent a nil or default value. + *z = Float{} + return nil + } + + if buf[0] != floatGobVersion { + return fmt.Errorf("Float.GobDecode: encoding version %d not supported", buf[0]) + } + + oldPrec := z.prec + oldMode := z.mode + + b := buf[1] + z.mode = RoundingMode((b >> 5) & 7) + z.acc = Accuracy((b>>3)&3) - 1 + z.form = form((b >> 1) & 3) + z.neg = b&1 != 0 + z.prec = binary.BigEndian.Uint32(buf[2:]) + + if z.form == finite { + z.exp = int32(binary.BigEndian.Uint32(buf[6:])) + z.mant = z.mant.setBytes(buf[10:]) + } + + if oldPrec != 0 { + z.mode = oldMode + z.SetPrec(uint(oldPrec)) + } + + return nil +} + +// MarshalText implements the encoding.TextMarshaler interface. +// Only the Float value is marshaled (in full precision), other +// attributes such as precision or accuracy are ignored. +func (x *Float) MarshalText() (text []byte, err error) { + if x == nil { + return []byte(""), nil + } + var buf []byte + return x.Append(buf, 'g', -1), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// The result is rounded per the precision and rounding mode of z. +// If z's precision is 0, it is changed to 64 before rounding takes +// effect. +func (z *Float) UnmarshalText(text []byte) error { + // TODO(gri): get rid of the []byte/string conversion + _, _, err := z.Parse(string(text), 0) + if err != nil { + err = fmt.Errorf("math/big: cannot unmarshal %q into a *big.Float (%v)", text, err) + } + return err +} diff --git a/vendor/github.com/golang/go/src/math/big/ftoa.go b/vendor/github.com/golang/go/src/math/big/ftoa.go new file mode 100644 index 000000000000..d2a85886c72d --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/ftoa.go @@ -0,0 +1,461 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements Float-to-string conversion functions. +// It is closely following the corresponding implementation +// in strconv/ftoa.go, but modified and simplified for Float. + +package big + +import ( + "bytes" + "fmt" + "strconv" +) + +// Text converts the floating-point number x to a string according +// to the given format and precision prec. The format is one of: +// +// 'e' -d.dddde±dd, decimal exponent, at least two (possibly 0) exponent digits +// 'E' -d.ddddE±dd, decimal exponent, at least two (possibly 0) exponent digits +// 'f' -ddddd.dddd, no exponent +// 'g' like 'e' for large exponents, like 'f' otherwise +// 'G' like 'E' for large exponents, like 'f' otherwise +// 'b' -ddddddp±dd, binary exponent +// 'p' -0x.dddp±dd, binary exponent, hexadecimal mantissa +// +// For the binary exponent formats, the mantissa is printed in normalized form: +// +// 'b' decimal integer mantissa using x.Prec() bits, or -0 +// 'p' hexadecimal fraction with 0.5 <= 0.mantissa < 1.0, or -0 +// +// If format is a different character, Text returns a "%" followed by the +// unrecognized format character. +// +// The precision prec controls the number of digits (excluding the exponent) +// printed by the 'e', 'E', 'f', 'g', and 'G' formats. For 'e', 'E', and 'f' +// it is the number of digits after the decimal point. For 'g' and 'G' it is +// the total number of digits. A negative precision selects the smallest +// number of decimal digits necessary to identify the value x uniquely using +// x.Prec() mantissa bits. +// The prec value is ignored for the 'b' or 'p' format. +func (x *Float) Text(format byte, prec int) string { + cap := 10 // TODO(gri) determine a good/better value here + if prec > 0 { + cap += prec + } + return string(x.Append(make([]byte, 0, cap), format, prec)) +} + +// String formats x like x.Text('g', 10). +// (String must be called explicitly, Float.Format does not support %s verb.) +func (x *Float) String() string { + return x.Text('g', 10) +} + +// Append appends to buf the string form of the floating-point number x, +// as generated by x.Text, and returns the extended buffer. +func (x *Float) Append(buf []byte, fmt byte, prec int) []byte { + // sign + if x.neg { + buf = append(buf, '-') + } + + // Inf + if x.form == inf { + if !x.neg { + buf = append(buf, '+') + } + return append(buf, "Inf"...) + } + + // pick off easy formats + switch fmt { + case 'b': + return x.fmtB(buf) + case 'p': + return x.fmtP(buf) + } + + // Algorithm: + // 1) convert Float to multiprecision decimal + // 2) round to desired precision + // 3) read digits out and format + + // 1) convert Float to multiprecision decimal + var d decimal // == 0.0 + if x.form == finite { + // x != 0 + d.init(x.mant, int(x.exp)-x.mant.bitLen()) + } + + // 2) round to desired precision + shortest := false + if prec < 0 { + shortest = true + roundShortest(&d, x) + // Precision for shortest representation mode. + switch fmt { + case 'e', 'E': + prec = len(d.mant) - 1 + case 'f': + prec = max(len(d.mant)-d.exp, 0) + case 'g', 'G': + prec = len(d.mant) + } + } else { + // round appropriately + switch fmt { + case 'e', 'E': + // one digit before and number of digits after decimal point + d.round(1 + prec) + case 'f': + // number of digits before and after decimal point + d.round(d.exp + prec) + case 'g', 'G': + if prec == 0 { + prec = 1 + } + d.round(prec) + } + } + + // 3) read digits out and format + switch fmt { + case 'e', 'E': + return fmtE(buf, fmt, prec, d) + case 'f': + return fmtF(buf, prec, d) + case 'g', 'G': + // trim trailing fractional zeros in %e format + eprec := prec + if eprec > len(d.mant) && len(d.mant) >= d.exp { + eprec = len(d.mant) + } + // %e is used if the exponent from the conversion + // is less than -4 or greater than or equal to the precision. + // If precision was the shortest possible, use eprec = 6 for + // this decision. + if shortest { + eprec = 6 + } + exp := d.exp - 1 + if exp < -4 || exp >= eprec { + if prec > len(d.mant) { + prec = len(d.mant) + } + return fmtE(buf, fmt+'e'-'g', prec-1, d) + } + if prec > d.exp { + prec = len(d.mant) + } + return fmtF(buf, max(prec-d.exp, 0), d) + } + + // unknown format + if x.neg { + buf = buf[:len(buf)-1] // sign was added prematurely - remove it again + } + return append(buf, '%', fmt) +} + +func roundShortest(d *decimal, x *Float) { + // if the mantissa is zero, the number is zero - stop now + if len(d.mant) == 0 { + return + } + + // Approach: All numbers in the interval [x - 1/2ulp, x + 1/2ulp] + // (possibly exclusive) round to x for the given precision of x. + // Compute the lower and upper bound in decimal form and find the + // shortest decimal number d such that lower <= d <= upper. + + // TODO(gri) strconv/ftoa.do describes a shortcut in some cases. + // See if we can use it (in adjusted form) here as well. + + // 1) Compute normalized mantissa mant and exponent exp for x such + // that the lsb of mant corresponds to 1/2 ulp for the precision of + // x (i.e., for mant we want x.prec + 1 bits). + mant := nat(nil).set(x.mant) + exp := int(x.exp) - mant.bitLen() + s := mant.bitLen() - int(x.prec+1) + switch { + case s < 0: + mant = mant.shl(mant, uint(-s)) + case s > 0: + mant = mant.shr(mant, uint(+s)) + } + exp += s + // x = mant * 2**exp with lsb(mant) == 1/2 ulp of x.prec + + // 2) Compute lower bound by subtracting 1/2 ulp. + var lower decimal + var tmp nat + lower.init(tmp.sub(mant, natOne), exp) + + // 3) Compute upper bound by adding 1/2 ulp. + var upper decimal + upper.init(tmp.add(mant, natOne), exp) + + // The upper and lower bounds are possible outputs only if + // the original mantissa is even, so that ToNearestEven rounding + // would round to the original mantissa and not the neighbors. + inclusive := mant[0]&2 == 0 // test bit 1 since original mantissa was shifted by 1 + + // Now we can figure out the minimum number of digits required. + // Walk along until d has distinguished itself from upper and lower. + for i, m := range d.mant { + l := lower.at(i) + u := upper.at(i) + + // Okay to round down (truncate) if lower has a different digit + // or if lower is inclusive and is exactly the result of rounding + // down (i.e., and we have reached the final digit of lower). + okdown := l != m || inclusive && i+1 == len(lower.mant) + + // Okay to round up if upper has a different digit and either upper + // is inclusive or upper is bigger than the result of rounding up. + okup := m != u && (inclusive || m+1 < u || i+1 < len(upper.mant)) + + // If it's okay to do either, then round to the nearest one. + // If it's okay to do only one, do it. + switch { + case okdown && okup: + d.round(i + 1) + return + case okdown: + d.roundDown(i + 1) + return + case okup: + d.roundUp(i + 1) + return + } + } +} + +// %e: d.ddddde±dd +func fmtE(buf []byte, fmt byte, prec int, d decimal) []byte { + // first digit + ch := byte('0') + if len(d.mant) > 0 { + ch = d.mant[0] + } + buf = append(buf, ch) + + // .moredigits + if prec > 0 { + buf = append(buf, '.') + i := 1 + m := min(len(d.mant), prec+1) + if i < m { + buf = append(buf, d.mant[i:m]...) + i = m + } + for ; i <= prec; i++ { + buf = append(buf, '0') + } + } + + // e± + buf = append(buf, fmt) + var exp int64 + if len(d.mant) > 0 { + exp = int64(d.exp) - 1 // -1 because first digit was printed before '.' + } + if exp < 0 { + ch = '-' + exp = -exp + } else { + ch = '+' + } + buf = append(buf, ch) + + // dd...d + if exp < 10 { + buf = append(buf, '0') // at least 2 exponent digits + } + return strconv.AppendInt(buf, exp, 10) +} + +// %f: ddddddd.ddddd +func fmtF(buf []byte, prec int, d decimal) []byte { + // integer, padded with zeros as needed + if d.exp > 0 { + m := min(len(d.mant), d.exp) + buf = append(buf, d.mant[:m]...) + for ; m < d.exp; m++ { + buf = append(buf, '0') + } + } else { + buf = append(buf, '0') + } + + // fraction + if prec > 0 { + buf = append(buf, '.') + for i := 0; i < prec; i++ { + buf = append(buf, d.at(d.exp+i)) + } + } + + return buf +} + +// fmtB appends the string of x in the format mantissa "p" exponent +// with a decimal mantissa and a binary exponent, or 0" if x is zero, +// and returns the extended buffer. +// The mantissa is normalized such that is uses x.Prec() bits in binary +// representation. +// The sign of x is ignored, and x must not be an Inf. +func (x *Float) fmtB(buf []byte) []byte { + if x.form == zero { + return append(buf, '0') + } + + if debugFloat && x.form != finite { + panic("non-finite float") + } + // x != 0 + + // adjust mantissa to use exactly x.prec bits + m := x.mant + switch w := uint32(len(x.mant)) * _W; { + case w < x.prec: + m = nat(nil).shl(m, uint(x.prec-w)) + case w > x.prec: + m = nat(nil).shr(m, uint(w-x.prec)) + } + + buf = append(buf, m.utoa(10)...) + buf = append(buf, 'p') + e := int64(x.exp) - int64(x.prec) + if e >= 0 { + buf = append(buf, '+') + } + return strconv.AppendInt(buf, e, 10) +} + +// fmtP appends the string of x in the format "0x." mantissa "p" exponent +// with a hexadecimal mantissa and a binary exponent, or "0" if x is zero, +// and returns the extended buffer. +// The mantissa is normalized such that 0.5 <= 0.mantissa < 1.0. +// The sign of x is ignored, and x must not be an Inf. +func (x *Float) fmtP(buf []byte) []byte { + if x.form == zero { + return append(buf, '0') + } + + if debugFloat && x.form != finite { + panic("non-finite float") + } + // x != 0 + + // remove trailing 0 words early + // (no need to convert to hex 0's and trim later) + m := x.mant + i := 0 + for i < len(m) && m[i] == 0 { + i++ + } + m = m[i:] + + buf = append(buf, "0x."...) + buf = append(buf, bytes.TrimRight(m.utoa(16), "0")...) + buf = append(buf, 'p') + if x.exp >= 0 { + buf = append(buf, '+') + } + return strconv.AppendInt(buf, int64(x.exp), 10) +} + +func min(x, y int) int { + if x < y { + return x + } + return y +} + +var _ fmt.Formatter = &floatZero // *Float must implement fmt.Formatter + +// Format implements fmt.Formatter. It accepts all the regular +// formats for floating-point numbers ('b', 'e', 'E', 'f', 'F', +// 'g', 'G') as well as 'p' and 'v'. See (*Float).Text for the +// interpretation of 'p'. The 'v' format is handled like 'g'. +// Format also supports specification of the minimum precision +// in digits, the output field width, as well as the format flags +// '+' and ' ' for sign control, '0' for space or zero padding, +// and '-' for left or right justification. See the fmt package +// for details. +func (x *Float) Format(s fmt.State, format rune) { + prec, hasPrec := s.Precision() + if !hasPrec { + prec = 6 // default precision for 'e', 'f' + } + + switch format { + case 'e', 'E', 'f', 'b', 'p': + // nothing to do + case 'F': + // (*Float).Text doesn't support 'F'; handle like 'f' + format = 'f' + case 'v': + // handle like 'g' + format = 'g' + fallthrough + case 'g', 'G': + if !hasPrec { + prec = -1 // default precision for 'g', 'G' + } + default: + fmt.Fprintf(s, "%%!%c(*big.Float=%s)", format, x.String()) + return + } + var buf []byte + buf = x.Append(buf, byte(format), prec) + if len(buf) == 0 { + buf = []byte("?") // should never happen, but don't crash + } + // len(buf) > 0 + + var sign string + switch { + case buf[0] == '-': + sign = "-" + buf = buf[1:] + case buf[0] == '+': + // +Inf + sign = "+" + if s.Flag(' ') { + sign = " " + } + buf = buf[1:] + case s.Flag('+'): + sign = "+" + case s.Flag(' '): + sign = " " + } + + var padding int + if width, hasWidth := s.Width(); hasWidth && width > len(sign)+len(buf) { + padding = width - len(sign) - len(buf) + } + + switch { + case s.Flag('0') && !x.IsInf(): + // 0-padding on left + writeMultiple(s, sign, 1) + writeMultiple(s, "0", padding) + s.Write(buf) + case s.Flag('-'): + // padding on right + writeMultiple(s, sign, 1) + s.Write(buf) + writeMultiple(s, " ", padding) + default: + // padding on left + writeMultiple(s, " ", padding) + writeMultiple(s, sign, 1) + s.Write(buf) + } +} diff --git a/vendor/github.com/golang/go/src/math/big/int.go b/vendor/github.com/golang/go/src/math/big/int.go new file mode 100644 index 000000000000..0eda9cd4e123 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/int.go @@ -0,0 +1,1033 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements signed multi-precision integers. + +package big + +import ( + "fmt" + "io" + "math/rand" + "strings" +) + +// An Int represents a signed multi-precision integer. +// The zero value for an Int represents the value 0. +type Int struct { + neg bool // sign + abs nat // absolute value of the integer +} + +var intOne = &Int{false, natOne} + +// Sign returns: +// +// -1 if x < 0 +// 0 if x == 0 +// +1 if x > 0 +// +func (x *Int) Sign() int { + if len(x.abs) == 0 { + return 0 + } + if x.neg { + return -1 + } + return 1 +} + +// SetInt64 sets z to x and returns z. +func (z *Int) SetInt64(x int64) *Int { + neg := false + if x < 0 { + neg = true + x = -x + } + z.abs = z.abs.setUint64(uint64(x)) + z.neg = neg + return z +} + +// SetUint64 sets z to x and returns z. +func (z *Int) SetUint64(x uint64) *Int { + z.abs = z.abs.setUint64(x) + z.neg = false + return z +} + +// NewInt allocates and returns a new Int set to x. +func NewInt(x int64) *Int { + return new(Int).SetInt64(x) +} + +// Set sets z to x and returns z. +func (z *Int) Set(x *Int) *Int { + if z != x { + z.abs = z.abs.set(x.abs) + z.neg = x.neg + } + return z +} + +// Bits provides raw (unchecked but fast) access to x by returning its +// absolute value as a little-endian Word slice. The result and x share +// the same underlying array. +// Bits is intended to support implementation of missing low-level Int +// functionality outside this package; it should be avoided otherwise. +func (x *Int) Bits() []Word { + return x.abs +} + +// SetBits provides raw (unchecked but fast) access to z by setting its +// value to abs, interpreted as a little-endian Word slice, and returning +// z. The result and abs share the same underlying array. +// SetBits is intended to support implementation of missing low-level Int +// functionality outside this package; it should be avoided otherwise. +func (z *Int) SetBits(abs []Word) *Int { + z.abs = nat(abs).norm() + z.neg = false + return z +} + +// Abs sets z to |x| (the absolute value of x) and returns z. +func (z *Int) Abs(x *Int) *Int { + z.Set(x) + z.neg = false + return z +} + +// Neg sets z to -x and returns z. +func (z *Int) Neg(x *Int) *Int { + z.Set(x) + z.neg = len(z.abs) > 0 && !z.neg // 0 has no sign + return z +} + +// Add sets z to the sum x+y and returns z. +func (z *Int) Add(x, y *Int) *Int { + neg := x.neg + if x.neg == y.neg { + // x + y == x + y + // (-x) + (-y) == -(x + y) + z.abs = z.abs.add(x.abs, y.abs) + } else { + // x + (-y) == x - y == -(y - x) + // (-x) + y == y - x == -(x - y) + if x.abs.cmp(y.abs) >= 0 { + z.abs = z.abs.sub(x.abs, y.abs) + } else { + neg = !neg + z.abs = z.abs.sub(y.abs, x.abs) + } + } + z.neg = len(z.abs) > 0 && neg // 0 has no sign + return z +} + +// Sub sets z to the difference x-y and returns z. +func (z *Int) Sub(x, y *Int) *Int { + neg := x.neg + if x.neg != y.neg { + // x - (-y) == x + y + // (-x) - y == -(x + y) + z.abs = z.abs.add(x.abs, y.abs) + } else { + // x - y == x - y == -(y - x) + // (-x) - (-y) == y - x == -(x - y) + if x.abs.cmp(y.abs) >= 0 { + z.abs = z.abs.sub(x.abs, y.abs) + } else { + neg = !neg + z.abs = z.abs.sub(y.abs, x.abs) + } + } + z.neg = len(z.abs) > 0 && neg // 0 has no sign + return z +} + +// Mul sets z to the product x*y and returns z. +func (z *Int) Mul(x, y *Int) *Int { + // x * y == x * y + // x * (-y) == -(x * y) + // (-x) * y == -(x * y) + // (-x) * (-y) == x * y + if x == y { + z.abs = z.abs.sqr(x.abs) + z.neg = false + return z + } + z.abs = z.abs.mul(x.abs, y.abs) + z.neg = len(z.abs) > 0 && x.neg != y.neg // 0 has no sign + return z +} + +// MulRange sets z to the product of all integers +// in the range [a, b] inclusively and returns z. +// If a > b (empty range), the result is 1. +func (z *Int) MulRange(a, b int64) *Int { + switch { + case a > b: + return z.SetInt64(1) // empty range + case a <= 0 && b >= 0: + return z.SetInt64(0) // range includes 0 + } + // a <= b && (b < 0 || a > 0) + + neg := false + if a < 0 { + neg = (b-a)&1 == 0 + a, b = -b, -a + } + + z.abs = z.abs.mulRange(uint64(a), uint64(b)) + z.neg = neg + return z +} + +// Binomial sets z to the binomial coefficient of (n, k) and returns z. +func (z *Int) Binomial(n, k int64) *Int { + // reduce the number of multiplications by reducing k + if n/2 < k && k <= n { + k = n - k // Binomial(n, k) == Binomial(n, n-k) + } + var a, b Int + a.MulRange(n-k+1, n) + b.MulRange(1, k) + return z.Quo(&a, &b) +} + +// Quo sets z to the quotient x/y for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// Quo implements truncated division (like Go); see QuoRem for more details. +func (z *Int) Quo(x, y *Int) *Int { + z.abs, _ = z.abs.div(nil, x.abs, y.abs) + z.neg = len(z.abs) > 0 && x.neg != y.neg // 0 has no sign + return z +} + +// Rem sets z to the remainder x%y for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// Rem implements truncated modulus (like Go); see QuoRem for more details. +func (z *Int) Rem(x, y *Int) *Int { + _, z.abs = nat(nil).div(z.abs, x.abs, y.abs) + z.neg = len(z.abs) > 0 && x.neg // 0 has no sign + return z +} + +// QuoRem sets z to the quotient x/y and r to the remainder x%y +// and returns the pair (z, r) for y != 0. +// If y == 0, a division-by-zero run-time panic occurs. +// +// QuoRem implements T-division and modulus (like Go): +// +// q = x/y with the result truncated to zero +// r = x - y*q +// +// (See Daan Leijen, ``Division and Modulus for Computer Scientists''.) +// See DivMod for Euclidean division and modulus (unlike Go). +// +func (z *Int) QuoRem(x, y, r *Int) (*Int, *Int) { + z.abs, r.abs = z.abs.div(r.abs, x.abs, y.abs) + z.neg, r.neg = len(z.abs) > 0 && x.neg != y.neg, len(r.abs) > 0 && x.neg // 0 has no sign + return z, r +} + +// Div sets z to the quotient x/y for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// Div implements Euclidean division (unlike Go); see DivMod for more details. +func (z *Int) Div(x, y *Int) *Int { + y_neg := y.neg // z may be an alias for y + var r Int + z.QuoRem(x, y, &r) + if r.neg { + if y_neg { + z.Add(z, intOne) + } else { + z.Sub(z, intOne) + } + } + return z +} + +// Mod sets z to the modulus x%y for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// Mod implements Euclidean modulus (unlike Go); see DivMod for more details. +func (z *Int) Mod(x, y *Int) *Int { + y0 := y // save y + if z == y || alias(z.abs, y.abs) { + y0 = new(Int).Set(y) + } + var q Int + q.QuoRem(x, y, z) + if z.neg { + if y0.neg { + z.Sub(z, y0) + } else { + z.Add(z, y0) + } + } + return z +} + +// DivMod sets z to the quotient x div y and m to the modulus x mod y +// and returns the pair (z, m) for y != 0. +// If y == 0, a division-by-zero run-time panic occurs. +// +// DivMod implements Euclidean division and modulus (unlike Go): +// +// q = x div y such that +// m = x - y*q with 0 <= m < |y| +// +// (See Raymond T. Boute, ``The Euclidean definition of the functions +// div and mod''. ACM Transactions on Programming Languages and +// Systems (TOPLAS), 14(2):127-144, New York, NY, USA, 4/1992. +// ACM press.) +// See QuoRem for T-division and modulus (like Go). +// +func (z *Int) DivMod(x, y, m *Int) (*Int, *Int) { + y0 := y // save y + if z == y || alias(z.abs, y.abs) { + y0 = new(Int).Set(y) + } + z.QuoRem(x, y, m) + if m.neg { + if y0.neg { + z.Add(z, intOne) + m.Sub(m, y0) + } else { + z.Sub(z, intOne) + m.Add(m, y0) + } + } + return z, m +} + +// Cmp compares x and y and returns: +// +// -1 if x < y +// 0 if x == y +// +1 if x > y +// +func (x *Int) Cmp(y *Int) (r int) { + // x cmp y == x cmp y + // x cmp (-y) == x + // (-x) cmp y == y + // (-x) cmp (-y) == -(x cmp y) + switch { + case x.neg == y.neg: + r = x.abs.cmp(y.abs) + if x.neg { + r = -r + } + case x.neg: + r = -1 + default: + r = 1 + } + return +} + +// CmpAbs compares the absolute values of x and y and returns: +// +// -1 if |x| < |y| +// 0 if |x| == |y| +// +1 if |x| > |y| +// +func (x *Int) CmpAbs(y *Int) int { + return x.abs.cmp(y.abs) +} + +// low32 returns the least significant 32 bits of x. +func low32(x nat) uint32 { + if len(x) == 0 { + return 0 + } + return uint32(x[0]) +} + +// low64 returns the least significant 64 bits of x. +func low64(x nat) uint64 { + if len(x) == 0 { + return 0 + } + v := uint64(x[0]) + if _W == 32 && len(x) > 1 { + return uint64(x[1])<<32 | v + } + return v +} + +// Int64 returns the int64 representation of x. +// If x cannot be represented in an int64, the result is undefined. +func (x *Int) Int64() int64 { + v := int64(low64(x.abs)) + if x.neg { + v = -v + } + return v +} + +// Uint64 returns the uint64 representation of x. +// If x cannot be represented in a uint64, the result is undefined. +func (x *Int) Uint64() uint64 { + return low64(x.abs) +} + +// IsInt64 reports whether x can be represented as an int64. +func (x *Int) IsInt64() bool { + if len(x.abs) <= 64/_W { + w := int64(low64(x.abs)) + return w >= 0 || x.neg && w == -w + } + return false +} + +// IsUint64 reports whether x can be represented as a uint64. +func (x *Int) IsUint64() bool { + return !x.neg && len(x.abs) <= 64/_W +} + +// SetString sets z to the value of s, interpreted in the given base, +// and returns z and a boolean indicating success. The entire string +// (not just a prefix) must be valid for success. If SetString fails, +// the value of z is undefined but the returned value is nil. +// +// The base argument must be 0 or a value between 2 and MaxBase. If the base +// is 0, the string prefix determines the actual conversion base. A prefix of +// ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a +// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10. +// +// For bases <= 36, lower and upper case letters are considered the same: +// The letters 'a' to 'z' and 'A' to 'Z' represent digit values 10 to 35. +// For bases > 36, the upper case letters 'A' to 'Z' represent the digit +// values 36 to 61. +// +func (z *Int) SetString(s string, base int) (*Int, bool) { + return z.setFromScanner(strings.NewReader(s), base) +} + +// setFromScanner implements SetString given an io.BytesScanner. +// For documentation see comments of SetString. +func (z *Int) setFromScanner(r io.ByteScanner, base int) (*Int, bool) { + if _, _, err := z.scan(r, base); err != nil { + return nil, false + } + // entire content must have been consumed + if _, err := r.ReadByte(); err != io.EOF { + return nil, false + } + return z, true // err == io.EOF => scan consumed all content of r +} + +// SetBytes interprets buf as the bytes of a big-endian unsigned +// integer, sets z to that value, and returns z. +func (z *Int) SetBytes(buf []byte) *Int { + z.abs = z.abs.setBytes(buf) + z.neg = false + return z +} + +// Bytes returns the absolute value of x as a big-endian byte slice. +func (x *Int) Bytes() []byte { + buf := make([]byte, len(x.abs)*_S) + return buf[x.abs.bytes(buf):] +} + +// BitLen returns the length of the absolute value of x in bits. +// The bit length of 0 is 0. +func (x *Int) BitLen() int { + return x.abs.bitLen() +} + +// Exp sets z = x**y mod |m| (i.e. the sign of m is ignored), and returns z. +// If y <= 0, the result is 1 mod |m|; if m == nil or m == 0, z = x**y. +// +// Modular exponentation of inputs of a particular size is not a +// cryptographically constant-time operation. +func (z *Int) Exp(x, y, m *Int) *Int { + // See Knuth, volume 2, section 4.6.3. + var yWords nat + if !y.neg { + yWords = y.abs + } + // y >= 0 + + var mWords nat + if m != nil { + mWords = m.abs // m.abs may be nil for m == 0 + } + + z.abs = z.abs.expNN(x.abs, yWords, mWords) + z.neg = len(z.abs) > 0 && x.neg && len(yWords) > 0 && yWords[0]&1 == 1 // 0 has no sign + if z.neg && len(mWords) > 0 { + // make modulus result positive + z.abs = z.abs.sub(mWords, z.abs) // z == x**y mod |m| && 0 <= z < |m| + z.neg = false + } + + return z +} + +// GCD sets z to the greatest common divisor of a and b, which both must +// be > 0, and returns z. +// If x or y are not nil, GCD sets their value such that z = a*x + b*y. +// If either a or b is <= 0, GCD sets z = x = y = 0. +func (z *Int) GCD(x, y, a, b *Int) *Int { + if a.Sign() <= 0 || b.Sign() <= 0 { + z.SetInt64(0) + if x != nil { + x.SetInt64(0) + } + if y != nil { + y.SetInt64(0) + } + return z + } + if x == nil && y == nil { + return z.lehmerGCD(a, b) + } + + A := new(Int).Set(a) + B := new(Int).Set(b) + + X := new(Int) + lastX := new(Int).SetInt64(1) + + q := new(Int) + temp := new(Int) + + r := new(Int) + for len(B.abs) > 0 { + q, r = q.QuoRem(A, B, r) + + A, B, r = B, r, A + + temp.Set(X) + X.Mul(X, q) + X.Sub(lastX, X) + lastX.Set(temp) + } + + if x != nil { + *x = *lastX + } + + if y != nil { + // y = (z - a*x)/b + y.Mul(a, lastX) + y.Sub(A, y) + y.Div(y, b) + } + + *z = *A + return z +} + +// lehmerGCD sets z to the greatest common divisor of a and b, +// which both must be > 0, and returns z. +// See Knuth, The Art of Computer Programming, Vol. 2, Section 4.5.2, Algorithm L. +// This implementation uses the improved condition by Collins requiring only one +// quotient and avoiding the possibility of single Word overflow. +// See Jebelean, "Improving the multiprecision Euclidean algorithm", +// Design and Implementation of Symbolic Computation Systems, pp 45-58. +func (z *Int) lehmerGCD(a, b *Int) *Int { + // ensure a >= b + if a.abs.cmp(b.abs) < 0 { + a, b = b, a + } + + // don't destroy incoming values of a and b + B := new(Int).Set(b) // must be set first in case b is an alias of z + A := z.Set(a) + + // temp variables for multiprecision update + t := new(Int) + r := new(Int) + s := new(Int) + w := new(Int) + + // loop invariant A >= B + for len(B.abs) > 1 { + // initialize the digits + var a1, a2, u0, u1, u2, v0, v1, v2 Word + + m := len(B.abs) // m >= 2 + n := len(A.abs) // n >= m >= 2 + + // extract the top Word of bits from A and B + h := nlz(A.abs[n-1]) + a1 = (A.abs[n-1] << h) | (A.abs[n-2] >> (_W - h)) + // B may have implicit zero words in the high bits if the lengths differ + switch { + case n == m: + a2 = (B.abs[n-1] << h) | (B.abs[n-2] >> (_W - h)) + case n == m+1: + a2 = (B.abs[n-2] >> (_W - h)) + default: + a2 = 0 + } + + // Since we are calculating with full words to avoid overflow, + // we use 'even' to track the sign of the cosequences. + // For even iterations: u0, v1 >= 0 && u1, v0 <= 0 + // For odd iterations: u0, v1 <= 0 && u1, v0 >= 0 + // The first iteration starts with k=1 (odd). + even := false + // variables to track the cosequences + u0, u1, u2 = 0, 1, 0 + v0, v1, v2 = 0, 0, 1 + + // Calculate the quotient and cosequences using Collins' stopping condition. + // Note that overflow of a Word is not possible when computing the remainder + // sequence and cosequences since the cosequence size is bounded by the input size. + // See section 4.2 of Jebelean for details. + for a2 >= v2 && a1-a2 >= v1+v2 { + q := a1 / a2 + a1, a2 = a2, a1-q*a2 + u0, u1, u2 = u1, u2, u1+q*u2 + v0, v1, v2 = v1, v2, v1+q*v2 + even = !even + } + + // multiprecision step + if v0 != 0 { + // simulate the effect of the single precision steps using the cosequences + // A = u0*A + v0*B + // B = u1*A + v1*B + + t.abs = t.abs.setWord(u0) + s.abs = s.abs.setWord(v0) + t.neg = !even + s.neg = even + + t.Mul(A, t) + s.Mul(B, s) + + r.abs = r.abs.setWord(u1) + w.abs = w.abs.setWord(v1) + r.neg = even + w.neg = !even + + r.Mul(A, r) + w.Mul(B, w) + + A.Add(t, s) + B.Add(r, w) + + } else { + // single-digit calculations failed to simluate any quotients + // do a standard Euclidean step + t.Rem(A, B) + A, B, t = B, t, A + } + } + + if len(B.abs) > 0 { + // standard Euclidean algorithm base case for B a single Word + if len(A.abs) > 1 { + // A is longer than a single Word + t.Rem(A, B) + A, B, t = B, t, A + } + if len(B.abs) > 0 { + // A and B are both a single Word + a1, a2 := A.abs[0], B.abs[0] + for a2 != 0 { + a1, a2 = a2, a1%a2 + } + A.abs[0] = a1 + } + } + *z = *A + return z +} + +// Rand sets z to a pseudo-random number in [0, n) and returns z. +// +// As this uses the math/rand package, it must not be used for +// security-sensitive work. Use crypto/rand.Int instead. +func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int { + z.neg = false + if n.neg || len(n.abs) == 0 { + z.abs = nil + return z + } + z.abs = z.abs.random(rnd, n.abs, n.abs.bitLen()) + return z +} + +// ModInverse sets z to the multiplicative inverse of g in the ring ℤ/nℤ +// and returns z. If g and n are not relatively prime, the result is undefined. +func (z *Int) ModInverse(g, n *Int) *Int { + if g.neg { + // GCD expects parameters a and b to be > 0. + var g2 Int + g = g2.Mod(g, n) + } + var d Int + d.GCD(z, nil, g, n) + // x and y are such that g*x + n*y = d. Since g and n are + // relatively prime, d = 1. Taking that modulo n results in + // g*x = 1, therefore x is the inverse element. + if z.neg { + z.Add(z, n) + } + return z +} + +// Jacobi returns the Jacobi symbol (x/y), either +1, -1, or 0. +// The y argument must be an odd integer. +func Jacobi(x, y *Int) int { + if len(y.abs) == 0 || y.abs[0]&1 == 0 { + panic(fmt.Sprintf("big: invalid 2nd argument to Int.Jacobi: need odd integer but got %s", y)) + } + + // We use the formulation described in chapter 2, section 2.4, + // "The Yacas Book of Algorithms": + // http://yacas.sourceforge.net/Algo.book.pdf + + var a, b, c Int + a.Set(x) + b.Set(y) + j := 1 + + if b.neg { + if a.neg { + j = -1 + } + b.neg = false + } + + for { + if b.Cmp(intOne) == 0 { + return j + } + if len(a.abs) == 0 { + return 0 + } + a.Mod(&a, &b) + if len(a.abs) == 0 { + return 0 + } + // a > 0 + + // handle factors of 2 in 'a' + s := a.abs.trailingZeroBits() + if s&1 != 0 { + bmod8 := b.abs[0] & 7 + if bmod8 == 3 || bmod8 == 5 { + j = -j + } + } + c.Rsh(&a, s) // a = 2^s*c + + // swap numerator and denominator + if b.abs[0]&3 == 3 && c.abs[0]&3 == 3 { + j = -j + } + a.Set(&b) + b.Set(&c) + } +} + +// modSqrt3Mod4 uses the identity +// (a^((p+1)/4))^2 mod p +// == u^(p+1) mod p +// == u^2 mod p +// to calculate the square root of any quadratic residue mod p quickly for 3 +// mod 4 primes. +func (z *Int) modSqrt3Mod4Prime(x, p *Int) *Int { + e := new(Int).Add(p, intOne) // e = p + 1 + e.Rsh(e, 2) // e = (p + 1) / 4 + z.Exp(x, e, p) // z = x^e mod p + return z +} + +// modSqrtTonelliShanks uses the Tonelli-Shanks algorithm to find the square +// root of a quadratic residue modulo any prime. +func (z *Int) modSqrtTonelliShanks(x, p *Int) *Int { + // Break p-1 into s*2^e such that s is odd. + var s Int + s.Sub(p, intOne) + e := s.abs.trailingZeroBits() + s.Rsh(&s, e) + + // find some non-square n + var n Int + n.SetInt64(2) + for Jacobi(&n, p) != -1 { + n.Add(&n, intOne) + } + + // Core of the Tonelli-Shanks algorithm. Follows the description in + // section 6 of "Square roots from 1; 24, 51, 10 to Dan Shanks" by Ezra + // Brown: + // https://www.maa.org/sites/default/files/pdf/upload_library/22/Polya/07468342.di020786.02p0470a.pdf + var y, b, g, t Int + y.Add(&s, intOne) + y.Rsh(&y, 1) + y.Exp(x, &y, p) // y = x^((s+1)/2) + b.Exp(x, &s, p) // b = x^s + g.Exp(&n, &s, p) // g = n^s + r := e + for { + // find the least m such that ord_p(b) = 2^m + var m uint + t.Set(&b) + for t.Cmp(intOne) != 0 { + t.Mul(&t, &t).Mod(&t, p) + m++ + } + + if m == 0 { + return z.Set(&y) + } + + t.SetInt64(0).SetBit(&t, int(r-m-1), 1).Exp(&g, &t, p) + // t = g^(2^(r-m-1)) mod p + g.Mul(&t, &t).Mod(&g, p) // g = g^(2^(r-m)) mod p + y.Mul(&y, &t).Mod(&y, p) + b.Mul(&b, &g).Mod(&b, p) + r = m + } +} + +// ModSqrt sets z to a square root of x mod p if such a square root exists, and +// returns z. The modulus p must be an odd prime. If x is not a square mod p, +// ModSqrt leaves z unchanged and returns nil. This function panics if p is +// not an odd integer. +func (z *Int) ModSqrt(x, p *Int) *Int { + switch Jacobi(x, p) { + case -1: + return nil // x is not a square mod p + case 0: + return z.SetInt64(0) // sqrt(0) mod p = 0 + case 1: + break + } + if x.neg || x.Cmp(p) >= 0 { // ensure 0 <= x < p + x = new(Int).Mod(x, p) + } + + // Check whether p is 3 mod 4, and if so, use the faster algorithm. + if len(p.abs) > 0 && p.abs[0]%4 == 3 { + return z.modSqrt3Mod4Prime(x, p) + } + // Otherwise, use Tonelli-Shanks. + return z.modSqrtTonelliShanks(x, p) +} + +// Lsh sets z = x << n and returns z. +func (z *Int) Lsh(x *Int, n uint) *Int { + z.abs = z.abs.shl(x.abs, n) + z.neg = x.neg + return z +} + +// Rsh sets z = x >> n and returns z. +func (z *Int) Rsh(x *Int, n uint) *Int { + if x.neg { + // (-x) >> s == ^(x-1) >> s == ^((x-1) >> s) == -(((x-1) >> s) + 1) + t := z.abs.sub(x.abs, natOne) // no underflow because |x| > 0 + t = t.shr(t, n) + z.abs = t.add(t, natOne) + z.neg = true // z cannot be zero if x is negative + return z + } + + z.abs = z.abs.shr(x.abs, n) + z.neg = false + return z +} + +// Bit returns the value of the i'th bit of x. That is, it +// returns (x>>i)&1. The bit index i must be >= 0. +func (x *Int) Bit(i int) uint { + if i == 0 { + // optimization for common case: odd/even test of x + if len(x.abs) > 0 { + return uint(x.abs[0] & 1) // bit 0 is same for -x + } + return 0 + } + if i < 0 { + panic("negative bit index") + } + if x.neg { + t := nat(nil).sub(x.abs, natOne) + return t.bit(uint(i)) ^ 1 + } + + return x.abs.bit(uint(i)) +} + +// SetBit sets z to x, with x's i'th bit set to b (0 or 1). +// That is, if b is 1 SetBit sets z = x | (1 << i); +// if b is 0 SetBit sets z = x &^ (1 << i). If b is not 0 or 1, +// SetBit will panic. +func (z *Int) SetBit(x *Int, i int, b uint) *Int { + if i < 0 { + panic("negative bit index") + } + if x.neg { + t := z.abs.sub(x.abs, natOne) + t = t.setBit(t, uint(i), b^1) + z.abs = t.add(t, natOne) + z.neg = len(z.abs) > 0 + return z + } + z.abs = z.abs.setBit(x.abs, uint(i), b) + z.neg = false + return z +} + +// And sets z = x & y and returns z. +func (z *Int) And(x, y *Int) *Int { + if x.neg == y.neg { + if x.neg { + // (-x) & (-y) == ^(x-1) & ^(y-1) == ^((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1) + x1 := nat(nil).sub(x.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) + z.abs = z.abs.add(z.abs.or(x1, y1), natOne) + z.neg = true // z cannot be zero if x and y are negative + return z + } + + // x & y == x & y + z.abs = z.abs.and(x.abs, y.abs) + z.neg = false + return z + } + + // x.neg != y.neg + if x.neg { + x, y = y, x // & is symmetric + } + + // x & (-y) == x & ^(y-1) == x &^ (y-1) + y1 := nat(nil).sub(y.abs, natOne) + z.abs = z.abs.andNot(x.abs, y1) + z.neg = false + return z +} + +// AndNot sets z = x &^ y and returns z. +func (z *Int) AndNot(x, y *Int) *Int { + if x.neg == y.neg { + if x.neg { + // (-x) &^ (-y) == ^(x-1) &^ ^(y-1) == ^(x-1) & (y-1) == (y-1) &^ (x-1) + x1 := nat(nil).sub(x.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) + z.abs = z.abs.andNot(y1, x1) + z.neg = false + return z + } + + // x &^ y == x &^ y + z.abs = z.abs.andNot(x.abs, y.abs) + z.neg = false + return z + } + + if x.neg { + // (-x) &^ y == ^(x-1) &^ y == ^(x-1) & ^y == ^((x-1) | y) == -(((x-1) | y) + 1) + x1 := nat(nil).sub(x.abs, natOne) + z.abs = z.abs.add(z.abs.or(x1, y.abs), natOne) + z.neg = true // z cannot be zero if x is negative and y is positive + return z + } + + // x &^ (-y) == x &^ ^(y-1) == x & (y-1) + y1 := nat(nil).sub(y.abs, natOne) + z.abs = z.abs.and(x.abs, y1) + z.neg = false + return z +} + +// Or sets z = x | y and returns z. +func (z *Int) Or(x, y *Int) *Int { + if x.neg == y.neg { + if x.neg { + // (-x) | (-y) == ^(x-1) | ^(y-1) == ^((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1) + x1 := nat(nil).sub(x.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) + z.abs = z.abs.add(z.abs.and(x1, y1), natOne) + z.neg = true // z cannot be zero if x and y are negative + return z + } + + // x | y == x | y + z.abs = z.abs.or(x.abs, y.abs) + z.neg = false + return z + } + + // x.neg != y.neg + if x.neg { + x, y = y, x // | is symmetric + } + + // x | (-y) == x | ^(y-1) == ^((y-1) &^ x) == -(^((y-1) &^ x) + 1) + y1 := nat(nil).sub(y.abs, natOne) + z.abs = z.abs.add(z.abs.andNot(y1, x.abs), natOne) + z.neg = true // z cannot be zero if one of x or y is negative + return z +} + +// Xor sets z = x ^ y and returns z. +func (z *Int) Xor(x, y *Int) *Int { + if x.neg == y.neg { + if x.neg { + // (-x) ^ (-y) == ^(x-1) ^ ^(y-1) == (x-1) ^ (y-1) + x1 := nat(nil).sub(x.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) + z.abs = z.abs.xor(x1, y1) + z.neg = false + return z + } + + // x ^ y == x ^ y + z.abs = z.abs.xor(x.abs, y.abs) + z.neg = false + return z + } + + // x.neg != y.neg + if x.neg { + x, y = y, x // ^ is symmetric + } + + // x ^ (-y) == x ^ ^(y-1) == ^(x ^ (y-1)) == -((x ^ (y-1)) + 1) + y1 := nat(nil).sub(y.abs, natOne) + z.abs = z.abs.add(z.abs.xor(x.abs, y1), natOne) + z.neg = true // z cannot be zero if only one of x or y is negative + return z +} + +// Not sets z = ^x and returns z. +func (z *Int) Not(x *Int) *Int { + if x.neg { + // ^(-x) == ^(^(x-1)) == x-1 + z.abs = z.abs.sub(x.abs, natOne) + z.neg = false + return z + } + + // ^x == -x-1 == -(x+1) + z.abs = z.abs.add(x.abs, natOne) + z.neg = true // z cannot be zero if x is positive + return z +} + +// Sqrt sets z to ⌊√x⌋, the largest integer such that z² ≤ x, and returns z. +// It panics if x is negative. +func (z *Int) Sqrt(x *Int) *Int { + if x.neg { + panic("square root of negative number") + } + z.neg = false + z.abs = z.abs.sqrt(x.abs) + return z +} diff --git a/vendor/github.com/golang/go/src/math/big/intconv.go b/vendor/github.com/golang/go/src/math/big/intconv.go new file mode 100644 index 000000000000..6cca827c8e34 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/intconv.go @@ -0,0 +1,247 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements int-to-string conversion functions. + +package big + +import ( + "errors" + "fmt" + "io" +) + +// Text returns the string representation of x in the given base. +// Base must be between 2 and 62, inclusive. The result uses the +// lower-case letters 'a' to 'z' for digit values 10 to 35, and +// the upper-case letters 'A' to 'Z' for digit values 36 to 61. +// No prefix (such as "0x") is added to the string. +func (x *Int) Text(base int) string { + if x == nil { + return "" + } + return string(x.abs.itoa(x.neg, base)) +} + +// Append appends the string representation of x, as generated by +// x.Text(base), to buf and returns the extended buffer. +func (x *Int) Append(buf []byte, base int) []byte { + if x == nil { + return append(buf, ""...) + } + return append(buf, x.abs.itoa(x.neg, base)...) +} + +func (x *Int) String() string { + return x.Text(10) +} + +// write count copies of text to s +func writeMultiple(s fmt.State, text string, count int) { + if len(text) > 0 { + b := []byte(text) + for ; count > 0; count-- { + s.Write(b) + } + } +} + +var _ fmt.Formatter = intOne // *Int must implement fmt.Formatter + +// Format implements fmt.Formatter. It accepts the formats +// 'b' (binary), 'o' (octal), 'd' (decimal), 'x' (lowercase +// hexadecimal), and 'X' (uppercase hexadecimal). +// Also supported are the full suite of package fmt's format +// flags for integral types, including '+' and ' ' for sign +// control, '#' for leading zero in octal and for hexadecimal, +// a leading "0x" or "0X" for "%#x" and "%#X" respectively, +// specification of minimum digits precision, output field +// width, space or zero padding, and '-' for left or right +// justification. +// +func (x *Int) Format(s fmt.State, ch rune) { + // determine base + var base int + switch ch { + case 'b': + base = 2 + case 'o': + base = 8 + case 'd', 's', 'v': + base = 10 + case 'x', 'X': + base = 16 + default: + // unknown format + fmt.Fprintf(s, "%%!%c(big.Int=%s)", ch, x.String()) + return + } + + if x == nil { + fmt.Fprint(s, "") + return + } + + // determine sign character + sign := "" + switch { + case x.neg: + sign = "-" + case s.Flag('+'): // supersedes ' ' when both specified + sign = "+" + case s.Flag(' '): + sign = " " + } + + // determine prefix characters for indicating output base + prefix := "" + if s.Flag('#') { + switch ch { + case 'o': // octal + prefix = "0" + case 'x': // hexadecimal + prefix = "0x" + case 'X': + prefix = "0X" + } + } + + digits := x.abs.utoa(base) + if ch == 'X' { + // faster than bytes.ToUpper + for i, d := range digits { + if 'a' <= d && d <= 'z' { + digits[i] = 'A' + (d - 'a') + } + } + } + + // number of characters for the three classes of number padding + var left int // space characters to left of digits for right justification ("%8d") + var zeros int // zero characters (actually cs[0]) as left-most digits ("%.8d") + var right int // space characters to right of digits for left justification ("%-8d") + + // determine number padding from precision: the least number of digits to output + precision, precisionSet := s.Precision() + if precisionSet { + switch { + case len(digits) < precision: + zeros = precision - len(digits) // count of zero padding + case len(digits) == 1 && digits[0] == '0' && precision == 0: + return // print nothing if zero value (x == 0) and zero precision ("." or ".0") + } + } + + // determine field pad from width: the least number of characters to output + length := len(sign) + len(prefix) + zeros + len(digits) + if width, widthSet := s.Width(); widthSet && length < width { // pad as specified + switch d := width - length; { + case s.Flag('-'): + // pad on the right with spaces; supersedes '0' when both specified + right = d + case s.Flag('0') && !precisionSet: + // pad with zeros unless precision also specified + zeros = d + default: + // pad on the left with spaces + left = d + } + } + + // print number as [left pad][sign][prefix][zero pad][digits][right pad] + writeMultiple(s, " ", left) + writeMultiple(s, sign, 1) + writeMultiple(s, prefix, 1) + writeMultiple(s, "0", zeros) + s.Write(digits) + writeMultiple(s, " ", right) +} + +// scan sets z to the integer value corresponding to the longest possible prefix +// read from r representing a signed integer number in a given conversion base. +// It returns z, the actual conversion base used, and an error, if any. In the +// error case, the value of z is undefined but the returned value is nil. The +// syntax follows the syntax of integer literals in Go. +// +// The base argument must be 0 or a value from 2 through MaxBase. If the base +// is 0, the string prefix determines the actual conversion base. A prefix of +// ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a +// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10. +// +func (z *Int) scan(r io.ByteScanner, base int) (*Int, int, error) { + // determine sign + neg, err := scanSign(r) + if err != nil { + return nil, 0, err + } + + // determine mantissa + z.abs, base, _, err = z.abs.scan(r, base, false) + if err != nil { + return nil, base, err + } + z.neg = len(z.abs) > 0 && neg // 0 has no sign + + return z, base, nil +} + +func scanSign(r io.ByteScanner) (neg bool, err error) { + var ch byte + if ch, err = r.ReadByte(); err != nil { + return false, err + } + switch ch { + case '-': + neg = true + case '+': + // nothing to do + default: + r.UnreadByte() + } + return +} + +// byteReader is a local wrapper around fmt.ScanState; +// it implements the ByteReader interface. +type byteReader struct { + fmt.ScanState +} + +func (r byteReader) ReadByte() (byte, error) { + ch, size, err := r.ReadRune() + if size != 1 && err == nil { + err = fmt.Errorf("invalid rune %#U", ch) + } + return byte(ch), err +} + +func (r byteReader) UnreadByte() error { + return r.UnreadRune() +} + +var _ fmt.Scanner = intOne // *Int must implement fmt.Scanner + +// Scan is a support routine for fmt.Scanner; it sets z to the value of +// the scanned number. It accepts the formats 'b' (binary), 'o' (octal), +// 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal). +func (z *Int) Scan(s fmt.ScanState, ch rune) error { + s.SkipSpace() // skip leading space characters + base := 0 + switch ch { + case 'b': + base = 2 + case 'o': + base = 8 + case 'd': + base = 10 + case 'x', 'X': + base = 16 + case 's', 'v': + // let scan determine the base + default: + return errors.New("Int.Scan: invalid verb") + } + _, _, err := z.scan(byteReader{s}, base) + return err +} diff --git a/vendor/github.com/golang/go/src/math/big/intmarsh.go b/vendor/github.com/golang/go/src/math/big/intmarsh.go new file mode 100644 index 000000000000..c1422e271072 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/intmarsh.go @@ -0,0 +1,80 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements encoding/decoding of Ints. + +package big + +import ( + "bytes" + "fmt" +) + +// Gob codec version. Permits backward-compatible changes to the encoding. +const intGobVersion byte = 1 + +// GobEncode implements the gob.GobEncoder interface. +func (x *Int) GobEncode() ([]byte, error) { + if x == nil { + return nil, nil + } + buf := make([]byte, 1+len(x.abs)*_S) // extra byte for version and sign bit + i := x.abs.bytes(buf) - 1 // i >= 0 + b := intGobVersion << 1 // make space for sign bit + if x.neg { + b |= 1 + } + buf[i] = b + return buf[i:], nil +} + +// GobDecode implements the gob.GobDecoder interface. +func (z *Int) GobDecode(buf []byte) error { + if len(buf) == 0 { + // Other side sent a nil or default value. + *z = Int{} + return nil + } + b := buf[0] + if b>>1 != intGobVersion { + return fmt.Errorf("Int.GobDecode: encoding version %d not supported", b>>1) + } + z.neg = b&1 != 0 + z.abs = z.abs.setBytes(buf[1:]) + return nil +} + +// MarshalText implements the encoding.TextMarshaler interface. +func (x *Int) MarshalText() (text []byte, err error) { + if x == nil { + return []byte(""), nil + } + return x.abs.itoa(x.neg, 10), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (z *Int) UnmarshalText(text []byte) error { + if _, ok := z.setFromScanner(bytes.NewReader(text), 0); !ok { + return fmt.Errorf("math/big: cannot unmarshal %q into a *big.Int", text) + } + return nil +} + +// The JSON marshalers are only here for API backward compatibility +// (programs that explicitly look for these two methods). JSON works +// fine with the TextMarshaler only. + +// MarshalJSON implements the json.Marshaler interface. +func (x *Int) MarshalJSON() ([]byte, error) { + return x.MarshalText() +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (z *Int) UnmarshalJSON(text []byte) error { + // Ignore null, like in the main JSON package. + if string(text) == "null" { + return nil + } + return z.UnmarshalText(text) +} diff --git a/vendor/github.com/golang/go/src/math/big/nat.go b/vendor/github.com/golang/go/src/math/big/nat.go new file mode 100644 index 000000000000..3bb818f5f259 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/nat.go @@ -0,0 +1,1267 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements unsigned multi-precision integers (natural +// numbers). They are the building blocks for the implementation +// of signed integers, rationals, and floating-point numbers. + +package big + +import ( + "math/bits" + "math/rand" + "sync" +) + +// An unsigned integer x of the form +// +// x = x[n-1]*_B^(n-1) + x[n-2]*_B^(n-2) + ... + x[1]*_B + x[0] +// +// with 0 <= x[i] < _B and 0 <= i < n is stored in a slice of length n, +// with the digits x[i] as the slice elements. +// +// A number is normalized if the slice contains no leading 0 digits. +// During arithmetic operations, denormalized values may occur but are +// always normalized before returning the final result. The normalized +// representation of 0 is the empty or nil slice (length = 0). +// +type nat []Word + +var ( + natOne = nat{1} + natTwo = nat{2} + natTen = nat{10} +) + +func (z nat) clear() { + for i := range z { + z[i] = 0 + } +} + +func (z nat) norm() nat { + i := len(z) + for i > 0 && z[i-1] == 0 { + i-- + } + return z[0:i] +} + +func (z nat) make(n int) nat { + if n <= cap(z) { + return z[:n] // reuse z + } + // Choosing a good value for e has significant performance impact + // because it increases the chance that a value can be reused. + const e = 4 // extra capacity + return make(nat, n, n+e) +} + +func (z nat) setWord(x Word) nat { + if x == 0 { + return z[:0] + } + z = z.make(1) + z[0] = x + return z +} + +func (z nat) setUint64(x uint64) nat { + // single-word value + if w := Word(x); uint64(w) == x { + return z.setWord(w) + } + // 2-word value + z = z.make(2) + z[1] = Word(x >> 32) + z[0] = Word(x) + return z +} + +func (z nat) set(x nat) nat { + z = z.make(len(x)) + copy(z, x) + return z +} + +func (z nat) add(x, y nat) nat { + m := len(x) + n := len(y) + + switch { + case m < n: + return z.add(y, x) + case m == 0: + // n == 0 because m >= n; result is 0 + return z[:0] + case n == 0: + // result is x + return z.set(x) + } + // m > 0 + + z = z.make(m + 1) + c := addVV(z[0:n], x, y) + if m > n { + c = addVW(z[n:m], x[n:], c) + } + z[m] = c + + return z.norm() +} + +func (z nat) sub(x, y nat) nat { + m := len(x) + n := len(y) + + switch { + case m < n: + panic("underflow") + case m == 0: + // n == 0 because m >= n; result is 0 + return z[:0] + case n == 0: + // result is x + return z.set(x) + } + // m > 0 + + z = z.make(m) + c := subVV(z[0:n], x, y) + if m > n { + c = subVW(z[n:], x[n:], c) + } + if c != 0 { + panic("underflow") + } + + return z.norm() +} + +func (x nat) cmp(y nat) (r int) { + m := len(x) + n := len(y) + if m != n || m == 0 { + switch { + case m < n: + r = -1 + case m > n: + r = 1 + } + return + } + + i := m - 1 + for i > 0 && x[i] == y[i] { + i-- + } + + switch { + case x[i] < y[i]: + r = -1 + case x[i] > y[i]: + r = 1 + } + return +} + +func (z nat) mulAddWW(x nat, y, r Word) nat { + m := len(x) + if m == 0 || y == 0 { + return z.setWord(r) // result is r + } + // m > 0 + + z = z.make(m + 1) + z[m] = mulAddVWW(z[0:m], x, y, r) + + return z.norm() +} + +// basicMul multiplies x and y and leaves the result in z. +// The (non-normalized) result is placed in z[0 : len(x) + len(y)]. +func basicMul(z, x, y nat) { + z[0 : len(x)+len(y)].clear() // initialize z + for i, d := range y { + if d != 0 { + z[len(x)+i] = addMulVVW(z[i:i+len(x)], x, d) + } + } +} + +// montgomery computes z mod m = x*y*2**(-n*_W) mod m, +// assuming k = -1/m mod 2**_W. +// z is used for storing the result which is returned; +// z must not alias x, y or m. +// See Gueron, "Efficient Software Implementations of Modular Exponentiation". +// https://eprint.iacr.org/2011/239.pdf +// In the terminology of that paper, this is an "Almost Montgomery Multiplication": +// x and y are required to satisfy 0 <= z < 2**(n*_W) and then the result +// z is guaranteed to satisfy 0 <= z < 2**(n*_W), but it may not be < m. +func (z nat) montgomery(x, y, m nat, k Word, n int) nat { + // This code assumes x, y, m are all the same length, n. + // (required by addMulVVW and the for loop). + // It also assumes that x, y are already reduced mod m, + // or else the result will not be properly reduced. + if len(x) != n || len(y) != n || len(m) != n { + panic("math/big: mismatched montgomery number lengths") + } + z = z.make(n) + z.clear() + var c Word + for i := 0; i < n; i++ { + d := y[i] + c2 := addMulVVW(z, x, d) + t := z[0] * k + c3 := addMulVVW(z, m, t) + copy(z, z[1:]) + cx := c + c2 + cy := cx + c3 + z[n-1] = cy + if cx < c2 || cy < c3 { + c = 1 + } else { + c = 0 + } + } + if c != 0 { + subVV(z, z, m) + } + return z +} + +// Fast version of z[0:n+n>>1].add(z[0:n+n>>1], x[0:n]) w/o bounds checks. +// Factored out for readability - do not use outside karatsuba. +func karatsubaAdd(z, x nat, n int) { + if c := addVV(z[0:n], z, x); c != 0 { + addVW(z[n:n+n>>1], z[n:], c) + } +} + +// Like karatsubaAdd, but does subtract. +func karatsubaSub(z, x nat, n int) { + if c := subVV(z[0:n], z, x); c != 0 { + subVW(z[n:n+n>>1], z[n:], c) + } +} + +// Operands that are shorter than karatsubaThreshold are multiplied using +// "grade school" multiplication; for longer operands the Karatsuba algorithm +// is used. +var karatsubaThreshold = 40 // computed by calibrate_test.go + +// karatsuba multiplies x and y and leaves the result in z. +// Both x and y must have the same length n and n must be a +// power of 2. The result vector z must have len(z) >= 6*n. +// The (non-normalized) result is placed in z[0 : 2*n]. +func karatsuba(z, x, y nat) { + n := len(y) + + // Switch to basic multiplication if numbers are odd or small. + // (n is always even if karatsubaThreshold is even, but be + // conservative) + if n&1 != 0 || n < karatsubaThreshold || n < 2 { + basicMul(z, x, y) + return + } + // n&1 == 0 && n >= karatsubaThreshold && n >= 2 + + // Karatsuba multiplication is based on the observation that + // for two numbers x and y with: + // + // x = x1*b + x0 + // y = y1*b + y0 + // + // the product x*y can be obtained with 3 products z2, z1, z0 + // instead of 4: + // + // x*y = x1*y1*b*b + (x1*y0 + x0*y1)*b + x0*y0 + // = z2*b*b + z1*b + z0 + // + // with: + // + // xd = x1 - x0 + // yd = y0 - y1 + // + // z1 = xd*yd + z2 + z0 + // = (x1-x0)*(y0 - y1) + z2 + z0 + // = x1*y0 - x1*y1 - x0*y0 + x0*y1 + z2 + z0 + // = x1*y0 - z2 - z0 + x0*y1 + z2 + z0 + // = x1*y0 + x0*y1 + + // split x, y into "digits" + n2 := n >> 1 // n2 >= 1 + x1, x0 := x[n2:], x[0:n2] // x = x1*b + y0 + y1, y0 := y[n2:], y[0:n2] // y = y1*b + y0 + + // z is used for the result and temporary storage: + // + // 6*n 5*n 4*n 3*n 2*n 1*n 0*n + // z = [z2 copy|z0 copy| xd*yd | yd:xd | x1*y1 | x0*y0 ] + // + // For each recursive call of karatsuba, an unused slice of + // z is passed in that has (at least) half the length of the + // caller's z. + + // compute z0 and z2 with the result "in place" in z + karatsuba(z, x0, y0) // z0 = x0*y0 + karatsuba(z[n:], x1, y1) // z2 = x1*y1 + + // compute xd (or the negative value if underflow occurs) + s := 1 // sign of product xd*yd + xd := z[2*n : 2*n+n2] + if subVV(xd, x1, x0) != 0 { // x1-x0 + s = -s + subVV(xd, x0, x1) // x0-x1 + } + + // compute yd (or the negative value if underflow occurs) + yd := z[2*n+n2 : 3*n] + if subVV(yd, y0, y1) != 0 { // y0-y1 + s = -s + subVV(yd, y1, y0) // y1-y0 + } + + // p = (x1-x0)*(y0-y1) == x1*y0 - x1*y1 - x0*y0 + x0*y1 for s > 0 + // p = (x0-x1)*(y0-y1) == x0*y0 - x0*y1 - x1*y0 + x1*y1 for s < 0 + p := z[n*3:] + karatsuba(p, xd, yd) + + // save original z2:z0 + // (ok to use upper half of z since we're done recursing) + r := z[n*4:] + copy(r, z[:n*2]) + + // add up all partial products + // + // 2*n n 0 + // z = [ z2 | z0 ] + // + [ z0 ] + // + [ z2 ] + // + [ p ] + // + karatsubaAdd(z[n2:], r, n) + karatsubaAdd(z[n2:], r[n:], n) + if s > 0 { + karatsubaAdd(z[n2:], p, n) + } else { + karatsubaSub(z[n2:], p, n) + } +} + +// alias reports whether x and y share the same base array. +func alias(x, y nat) bool { + return cap(x) > 0 && cap(y) > 0 && &x[0:cap(x)][cap(x)-1] == &y[0:cap(y)][cap(y)-1] +} + +// addAt implements z += x<<(_W*i); z must be long enough. +// (we don't use nat.add because we need z to stay the same +// slice, and we don't need to normalize z after each addition) +func addAt(z, x nat, i int) { + if n := len(x); n > 0 { + if c := addVV(z[i:i+n], z[i:], x); c != 0 { + j := i + n + if j < len(z) { + addVW(z[j:], z[j:], c) + } + } + } +} + +func max(x, y int) int { + if x > y { + return x + } + return y +} + +// karatsubaLen computes an approximation to the maximum k <= n such that +// k = p<= 0. Thus, the +// result is the largest number that can be divided repeatedly by 2 before +// becoming about the value of karatsubaThreshold. +func karatsubaLen(n int) int { + i := uint(0) + for n > karatsubaThreshold { + n >>= 1 + i++ + } + return n << i +} + +func (z nat) mul(x, y nat) nat { + m := len(x) + n := len(y) + + switch { + case m < n: + return z.mul(y, x) + case m == 0 || n == 0: + return z[:0] + case n == 1: + return z.mulAddWW(x, y[0], 0) + } + // m >= n > 1 + + // determine if z can be reused + if alias(z, x) || alias(z, y) { + z = nil // z is an alias for x or y - cannot reuse + } + + // use basic multiplication if the numbers are small + if n < karatsubaThreshold { + z = z.make(m + n) + basicMul(z, x, y) + return z.norm() + } + // m >= n && n >= karatsubaThreshold && n >= 2 + + // determine Karatsuba length k such that + // + // x = xh*b + x0 (0 <= x0 < b) + // y = yh*b + y0 (0 <= y0 < b) + // b = 1<<(_W*k) ("base" of digits xi, yi) + // + k := karatsubaLen(n) + // k <= n + + // multiply x0 and y0 via Karatsuba + x0 := x[0:k] // x0 is not normalized + y0 := y[0:k] // y0 is not normalized + z = z.make(max(6*k, m+n)) // enough space for karatsuba of x0*y0 and full result of x*y + karatsuba(z, x0, y0) + z = z[0 : m+n] // z has final length but may be incomplete + z[2*k:].clear() // upper portion of z is garbage (and 2*k <= m+n since k <= n <= m) + + // If xh != 0 or yh != 0, add the missing terms to z. For + // + // xh = xi*b^i + ... + x2*b^2 + x1*b (0 <= xi < b) + // yh = y1*b (0 <= y1 < b) + // + // the missing terms are + // + // x0*y1*b and xi*y0*b^i, xi*y1*b^(i+1) for i > 0 + // + // since all the yi for i > 1 are 0 by choice of k: If any of them + // were > 0, then yh >= b^2 and thus y >= b^2. Then k' = k*2 would + // be a larger valid threshold contradicting the assumption about k. + // + if k < n || m != n { + var t nat + + // add x0*y1*b + x0 := x0.norm() + y1 := y[k:] // y1 is normalized because y is + t = t.mul(x0, y1) // update t so we don't lose t's underlying array + addAt(z, t, k) + + // add xi*y0< k { + xi = xi[:k] + } + xi = xi.norm() + t = t.mul(xi, y0) + addAt(z, t, i) + t = t.mul(xi, y1) + addAt(z, t, i+k) + } + } + + return z.norm() +} + +// basicSqr sets z = x*x and is asymptotically faster than basicMul +// by about a factor of 2, but slower for small arguments due to overhead. +// Requirements: len(x) > 0, len(z) >= 2*len(x) +// The (non-normalized) result is placed in z[0 : 2 * len(x)]. +func basicSqr(z, x nat) { + n := len(x) + t := make(nat, 2*n) // temporary variable to hold the products + z[1], z[0] = mulWW(x[0], x[0]) // the initial square + for i := 1; i < n; i++ { + d := x[i] + // z collects the squares x[i] * x[i] + z[2*i+1], z[2*i] = mulWW(d, d) + // t collects the products x[i] * x[j] where j < i + t[2*i] = addMulVVW(t[i:2*i], x[0:i], d) + } + t[2*n-1] = shlVU(t[1:2*n-1], t[1:2*n-1], 1) // double the j < i products + addVV(z, z, t) // combine the result +} + +// Operands that are shorter than basicSqrThreshold are squared using +// "grade school" multiplication; for operands longer than karatsubaSqrThreshold +// the Karatsuba algorithm is used. +var basicSqrThreshold = 20 // computed by calibrate_test.go +var karatsubaSqrThreshold = 400 // computed by calibrate_test.go + +// z = x*x +func (z nat) sqr(x nat) nat { + n := len(x) + switch { + case n == 0: + return z[:0] + case n == 1: + d := x[0] + z = z.make(2) + z[1], z[0] = mulWW(d, d) + return z.norm() + } + + if alias(z, x) { + z = nil // z is an alias for x - cannot reuse + } + z = z.make(2 * n) + + if n < basicSqrThreshold { + basicMul(z, x, x) + return z.norm() + } + if n < karatsubaSqrThreshold { + basicSqr(z, x) + return z.norm() + } + + return z.mul(x, x) +} + +// mulRange computes the product of all the unsigned integers in the +// range [a, b] inclusively. If a > b (empty range), the result is 1. +func (z nat) mulRange(a, b uint64) nat { + switch { + case a == 0: + // cut long ranges short (optimization) + return z.setUint64(0) + case a > b: + return z.setUint64(1) + case a == b: + return z.setUint64(a) + case a+1 == b: + return z.mul(nat(nil).setUint64(a), nat(nil).setUint64(b)) + } + m := (a + b) / 2 + return z.mul(nat(nil).mulRange(a, m), nat(nil).mulRange(m+1, b)) +} + +// q = (x-r)/y, with 0 <= r < y +func (z nat) divW(x nat, y Word) (q nat, r Word) { + m := len(x) + switch { + case y == 0: + panic("division by zero") + case y == 1: + q = z.set(x) // result is x + return + case m == 0: + q = z[:0] // result is 0 + return + } + // m > 0 + z = z.make(m) + r = divWVW(z, 0, x, y) + q = z.norm() + return +} + +func (z nat) div(z2, u, v nat) (q, r nat) { + if len(v) == 0 { + panic("division by zero") + } + + if u.cmp(v) < 0 { + q = z[:0] + r = z2.set(u) + return + } + + if len(v) == 1 { + var r2 Word + q, r2 = z.divW(u, v[0]) + r = z2.setWord(r2) + return + } + + q, r = z.divLarge(z2, u, v) + return +} + +// getNat returns a *nat of len n. The contents may not be zero. +// The pool holds *nat to avoid allocation when converting to interface{}. +func getNat(n int) *nat { + var z *nat + if v := natPool.Get(); v != nil { + z = v.(*nat) + } + if z == nil { + z = new(nat) + } + *z = z.make(n) + return z +} + +func putNat(x *nat) { + natPool.Put(x) +} + +var natPool sync.Pool + +// q = (uIn-r)/v, with 0 <= r < y +// Uses z as storage for q, and u as storage for r if possible. +// See Knuth, Volume 2, section 4.3.1, Algorithm D. +// Preconditions: +// len(v) >= 2 +// len(uIn) >= len(v) +func (z nat) divLarge(u, uIn, v nat) (q, r nat) { + n := len(v) + m := len(uIn) - n + + // determine if z can be reused + // TODO(gri) should find a better solution - this if statement + // is very costly (see e.g. time pidigits -s -n 10000) + if alias(z, u) || alias(z, uIn) || alias(z, v) { + z = nil // z is an alias for u or uIn or v - cannot reuse + } + q = z.make(m + 1) + + qhatvp := getNat(n + 1) + qhatv := *qhatvp + if alias(u, uIn) || alias(u, v) { + u = nil // u is an alias for uIn or v - cannot reuse + } + u = u.make(len(uIn) + 1) + u.clear() // TODO(gri) no need to clear if we allocated a new u + + // D1. + var v1p *nat + shift := nlz(v[n-1]) + if shift > 0 { + // do not modify v, it may be used by another goroutine simultaneously + v1p = getNat(n) + v1 := *v1p + shlVU(v1, v, shift) + v = v1 + } + u[len(uIn)] = shlVU(u[0:len(uIn)], uIn, shift) + + // D2. + vn1 := v[n-1] + for j := m; j >= 0; j-- { + // D3. + qhat := Word(_M) + if ujn := u[j+n]; ujn != vn1 { + var rhat Word + qhat, rhat = divWW(ujn, u[j+n-1], vn1) + + // x1 | x2 = q̂v_{n-2} + vn2 := v[n-2] + x1, x2 := mulWW(qhat, vn2) + // test if q̂v_{n-2} > br̂ + u_{j+n-2} + ujn2 := u[j+n-2] + for greaterThan(x1, x2, rhat, ujn2) { + qhat-- + prevRhat := rhat + rhat += vn1 + // v[n-1] >= 0, so this tests for overflow. + if rhat < prevRhat { + break + } + x1, x2 = mulWW(qhat, vn2) + } + } + + // D4. + qhatv[n] = mulAddVWW(qhatv[0:n], v, qhat, 0) + + c := subVV(u[j:j+len(qhatv)], u[j:], qhatv) + if c != 0 { + c := addVV(u[j:j+n], u[j:], v) + u[j+n] += c + qhat-- + } + + q[j] = qhat + } + if v1p != nil { + putNat(v1p) + } + putNat(qhatvp) + + q = q.norm() + shrVU(u, u, shift) + r = u.norm() + + return q, r +} + +// Length of x in bits. x must be normalized. +func (x nat) bitLen() int { + if i := len(x) - 1; i >= 0 { + return i*_W + bits.Len(uint(x[i])) + } + return 0 +} + +// trailingZeroBits returns the number of consecutive least significant zero +// bits of x. +func (x nat) trailingZeroBits() uint { + if len(x) == 0 { + return 0 + } + var i uint + for x[i] == 0 { + i++ + } + // x[i] != 0 + return i*_W + uint(bits.TrailingZeros(uint(x[i]))) +} + +// z = x << s +func (z nat) shl(x nat, s uint) nat { + m := len(x) + if m == 0 { + return z[:0] + } + // m > 0 + + n := m + int(s/_W) + z = z.make(n + 1) + z[n] = shlVU(z[n-m:n], x, s%_W) + z[0 : n-m].clear() + + return z.norm() +} + +// z = x >> s +func (z nat) shr(x nat, s uint) nat { + m := len(x) + n := m - int(s/_W) + if n <= 0 { + return z[:0] + } + // n > 0 + + z = z.make(n) + shrVU(z, x[m-n:], s%_W) + + return z.norm() +} + +func (z nat) setBit(x nat, i uint, b uint) nat { + j := int(i / _W) + m := Word(1) << (i % _W) + n := len(x) + switch b { + case 0: + z = z.make(n) + copy(z, x) + if j >= n { + // no need to grow + return z + } + z[j] &^= m + return z.norm() + case 1: + if j >= n { + z = z.make(j + 1) + z[n:].clear() + } else { + z = z.make(n) + } + copy(z, x) + z[j] |= m + // no need to normalize + return z + } + panic("set bit is not 0 or 1") +} + +// bit returns the value of the i'th bit, with lsb == bit 0. +func (x nat) bit(i uint) uint { + j := i / _W + if j >= uint(len(x)) { + return 0 + } + // 0 <= j < len(x) + return uint(x[j] >> (i % _W) & 1) +} + +// sticky returns 1 if there's a 1 bit within the +// i least significant bits, otherwise it returns 0. +func (x nat) sticky(i uint) uint { + j := i / _W + if j >= uint(len(x)) { + if len(x) == 0 { + return 0 + } + return 1 + } + // 0 <= j < len(x) + for _, x := range x[:j] { + if x != 0 { + return 1 + } + } + if x[j]<<(_W-i%_W) != 0 { + return 1 + } + return 0 +} + +func (z nat) and(x, y nat) nat { + m := len(x) + n := len(y) + if m > n { + m = n + } + // m <= n + + z = z.make(m) + for i := 0; i < m; i++ { + z[i] = x[i] & y[i] + } + + return z.norm() +} + +func (z nat) andNot(x, y nat) nat { + m := len(x) + n := len(y) + if n > m { + n = m + } + // m >= n + + z = z.make(m) + for i := 0; i < n; i++ { + z[i] = x[i] &^ y[i] + } + copy(z[n:m], x[n:m]) + + return z.norm() +} + +func (z nat) or(x, y nat) nat { + m := len(x) + n := len(y) + s := x + if m < n { + n, m = m, n + s = y + } + // m >= n + + z = z.make(m) + for i := 0; i < n; i++ { + z[i] = x[i] | y[i] + } + copy(z[n:m], s[n:m]) + + return z.norm() +} + +func (z nat) xor(x, y nat) nat { + m := len(x) + n := len(y) + s := x + if m < n { + n, m = m, n + s = y + } + // m >= n + + z = z.make(m) + for i := 0; i < n; i++ { + z[i] = x[i] ^ y[i] + } + copy(z[n:m], s[n:m]) + + return z.norm() +} + +// greaterThan reports whether (x1<<_W + x2) > (y1<<_W + y2) +func greaterThan(x1, x2, y1, y2 Word) bool { + return x1 > y1 || x1 == y1 && x2 > y2 +} + +// modW returns x % d. +func (x nat) modW(d Word) (r Word) { + // TODO(agl): we don't actually need to store the q value. + var q nat + q = q.make(len(x)) + return divWVW(q, 0, x, d) +} + +// random creates a random integer in [0..limit), using the space in z if +// possible. n is the bit length of limit. +func (z nat) random(rand *rand.Rand, limit nat, n int) nat { + if alias(z, limit) { + z = nil // z is an alias for limit - cannot reuse + } + z = z.make(len(limit)) + + bitLengthOfMSW := uint(n % _W) + if bitLengthOfMSW == 0 { + bitLengthOfMSW = _W + } + mask := Word((1 << bitLengthOfMSW) - 1) + + for { + switch _W { + case 32: + for i := range z { + z[i] = Word(rand.Uint32()) + } + case 64: + for i := range z { + z[i] = Word(rand.Uint32()) | Word(rand.Uint32())<<32 + } + default: + panic("unknown word size") + } + z[len(limit)-1] &= mask + if z.cmp(limit) < 0 { + break + } + } + + return z.norm() +} + +// If m != 0 (i.e., len(m) != 0), expNN sets z to x**y mod m; +// otherwise it sets z to x**y. The result is the value of z. +func (z nat) expNN(x, y, m nat) nat { + if alias(z, x) || alias(z, y) { + // We cannot allow in-place modification of x or y. + z = nil + } + + // x**y mod 1 == 0 + if len(m) == 1 && m[0] == 1 { + return z.setWord(0) + } + // m == 0 || m > 1 + + // x**0 == 1 + if len(y) == 0 { + return z.setWord(1) + } + // y > 0 + + // x**1 mod m == x mod m + if len(y) == 1 && y[0] == 1 && len(m) != 0 { + _, z = z.div(z, x, m) + return z + } + // y > 1 + + if len(m) != 0 { + // We likely end up being as long as the modulus. + z = z.make(len(m)) + } + z = z.set(x) + + // If the base is non-trivial and the exponent is large, we use + // 4-bit, windowed exponentiation. This involves precomputing 14 values + // (x^2...x^15) but then reduces the number of multiply-reduces by a + // third. Even for a 32-bit exponent, this reduces the number of + // operations. Uses Montgomery method for odd moduli. + if x.cmp(natOne) > 0 && len(y) > 1 && len(m) > 0 { + if m[0]&1 == 1 { + return z.expNNMontgomery(x, y, m) + } + return z.expNNWindowed(x, y, m) + } + + v := y[len(y)-1] // v > 0 because y is normalized and y > 0 + shift := nlz(v) + 1 + v <<= shift + var q nat + + const mask = 1 << (_W - 1) + + // We walk through the bits of the exponent one by one. Each time we + // see a bit, we square, thus doubling the power. If the bit is a one, + // we also multiply by x, thus adding one to the power. + + w := _W - int(shift) + // zz and r are used to avoid allocating in mul and div as + // otherwise the arguments would alias. + var zz, r nat + for j := 0; j < w; j++ { + zz = zz.sqr(z) + zz, z = z, zz + + if v&mask != 0 { + zz = zz.mul(z, x) + zz, z = z, zz + } + + if len(m) != 0 { + zz, r = zz.div(r, z, m) + zz, r, q, z = q, z, zz, r + } + + v <<= 1 + } + + for i := len(y) - 2; i >= 0; i-- { + v = y[i] + + for j := 0; j < _W; j++ { + zz = zz.sqr(z) + zz, z = z, zz + + if v&mask != 0 { + zz = zz.mul(z, x) + zz, z = z, zz + } + + if len(m) != 0 { + zz, r = zz.div(r, z, m) + zz, r, q, z = q, z, zz, r + } + + v <<= 1 + } + } + + return z.norm() +} + +// expNNWindowed calculates x**y mod m using a fixed, 4-bit window. +func (z nat) expNNWindowed(x, y, m nat) nat { + // zz and r are used to avoid allocating in mul and div as otherwise + // the arguments would alias. + var zz, r nat + + const n = 4 + // powers[i] contains x^i. + var powers [1 << n]nat + powers[0] = natOne + powers[1] = x + for i := 2; i < 1<= 0; i-- { + yi := y[i] + for j := 0; j < _W; j += n { + if i != len(y)-1 || j != 0 { + // Unrolled loop for significant performance + // gain. Use go test -bench=".*" in crypto/rsa + // to check performance before making changes. + zz = zz.sqr(z) + zz, z = z, zz + zz, r = zz.div(r, z, m) + z, r = r, z + + zz = zz.sqr(z) + zz, z = z, zz + zz, r = zz.div(r, z, m) + z, r = r, z + + zz = zz.sqr(z) + zz, z = z, zz + zz, r = zz.div(r, z, m) + z, r = r, z + + zz = zz.sqr(z) + zz, z = z, zz + zz, r = zz.div(r, z, m) + z, r = r, z + } + + zz = zz.mul(z, powers[yi>>(_W-n)]) + zz, z = z, zz + zz, r = zz.div(r, z, m) + z, r = r, z + + yi <<= n + } + } + + return z.norm() +} + +// expNNMontgomery calculates x**y mod m using a fixed, 4-bit window. +// Uses Montgomery representation. +func (z nat) expNNMontgomery(x, y, m nat) nat { + numWords := len(m) + + // We want the lengths of x and m to be equal. + // It is OK if x >= m as long as len(x) == len(m). + if len(x) > numWords { + _, x = nat(nil).div(nil, x, m) + // Note: now len(x) <= numWords, not guaranteed ==. + } + if len(x) < numWords { + rr := make(nat, numWords) + copy(rr, x) + x = rr + } + + // Ideally the precomputations would be performed outside, and reused + // k0 = -m**-1 mod 2**_W. Algorithm from: Dumas, J.G. "On Newton–Raphson + // Iteration for Multiplicative Inverses Modulo Prime Powers". + k0 := 2 - m[0] + t := m[0] - 1 + for i := 1; i < _W; i <<= 1 { + t *= t + k0 *= (t + 1) + } + k0 = -k0 + + // RR = 2**(2*_W*len(m)) mod m + RR := nat(nil).setWord(1) + zz := nat(nil).shl(RR, uint(2*numWords*_W)) + _, RR = RR.div(RR, zz, m) + if len(RR) < numWords { + zz = zz.make(numWords) + copy(zz, RR) + RR = zz + } + // one = 1, with equal length to that of m + one := make(nat, numWords) + one[0] = 1 + + const n = 4 + // powers[i] contains x^i + var powers [1 << n]nat + powers[0] = powers[0].montgomery(one, RR, m, k0, numWords) + powers[1] = powers[1].montgomery(x, RR, m, k0, numWords) + for i := 2; i < 1<= 0; i-- { + yi := y[i] + for j := 0; j < _W; j += n { + if i != len(y)-1 || j != 0 { + zz = zz.montgomery(z, z, m, k0, numWords) + z = z.montgomery(zz, zz, m, k0, numWords) + zz = zz.montgomery(z, z, m, k0, numWords) + z = z.montgomery(zz, zz, m, k0, numWords) + } + zz = zz.montgomery(z, powers[yi>>(_W-n)], m, k0, numWords) + z, zz = zz, z + yi <<= n + } + } + // convert to regular number + zz = zz.montgomery(z, one, m, k0, numWords) + + // One last reduction, just in case. + // See golang.org/issue/13907. + if zz.cmp(m) >= 0 { + // Common case is m has high bit set; in that case, + // since zz is the same length as m, there can be just + // one multiple of m to remove. Just subtract. + // We think that the subtract should be sufficient in general, + // so do that unconditionally, but double-check, + // in case our beliefs are wrong. + // The div is not expected to be reached. + zz = zz.sub(zz, m) + if zz.cmp(m) >= 0 { + _, zz = nat(nil).div(nil, zz, m) + } + } + + return zz.norm() +} + +// bytes writes the value of z into buf using big-endian encoding. +// len(buf) must be >= len(z)*_S. The value of z is encoded in the +// slice buf[i:]. The number i of unused bytes at the beginning of +// buf is returned as result. +func (z nat) bytes(buf []byte) (i int) { + i = len(buf) + for _, d := range z { + for j := 0; j < _S; j++ { + i-- + buf[i] = byte(d) + d >>= 8 + } + } + + for i < len(buf) && buf[i] == 0 { + i++ + } + + return +} + +// setBytes interprets buf as the bytes of a big-endian unsigned +// integer, sets z to that value, and returns z. +func (z nat) setBytes(buf []byte) nat { + z = z.make((len(buf) + _S - 1) / _S) + + k := 0 + s := uint(0) + var d Word + for i := len(buf); i > 0; i-- { + d |= Word(buf[i-1]) << s + if s += 8; s == _S*8 { + z[k] = d + k++ + s = 0 + d = 0 + } + } + if k < len(z) { + z[k] = d + } + + return z.norm() +} + +// sqrt sets z = ⌊√x⌋ +func (z nat) sqrt(x nat) nat { + if x.cmp(natOne) <= 0 { + return z.set(x) + } + if alias(z, x) { + z = nil + } + + // Start with value known to be too large and repeat "z = ⌊(z + ⌊x/z⌋)/2⌋" until it stops getting smaller. + // See Brent and Zimmermann, Modern Computer Arithmetic, Algorithm 1.13 (SqrtInt). + // https://members.loria.fr/PZimmermann/mca/pub226.html + // If x is one less than a perfect square, the sequence oscillates between the correct z and z+1; + // otherwise it converges to the correct z and stays there. + var z1, z2 nat + z1 = z + z1 = z1.setUint64(1) + z1 = z1.shl(z1, uint(x.bitLen()/2+1)) // must be ≥ √x + for n := 0; ; n++ { + z2, _ = z2.div(nil, x, z1) + z2 = z2.add(z2, z1) + z2 = z2.shr(z2, 1) + if z2.cmp(z1) >= 0 { + // z1 is answer. + // Figure out whether z1 or z2 is currently aliased to z by looking at loop count. + if n&1 == 0 { + return z1 + } + return z.set(z1) + } + z1, z2 = z2, z1 + } +} diff --git a/vendor/github.com/golang/go/src/math/big/natconv.go b/vendor/github.com/golang/go/src/math/big/natconv.go new file mode 100644 index 000000000000..21ccbd6cfafc --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/natconv.go @@ -0,0 +1,503 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements nat-to-string conversion functions. + +package big + +import ( + "errors" + "fmt" + "io" + "math" + "math/bits" + "sync" +) + +const digits = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + +// Note: MaxBase = len(digits), but it must remain an untyped rune constant +// for API compatibility. + +// MaxBase is the largest number base accepted for string conversions. +const MaxBase = 10 + ('z' - 'a' + 1) + ('Z' - 'A' + 1) +const maxBaseSmall = 10 + ('z' - 'a' + 1) + +// maxPow returns (b**n, n) such that b**n is the largest power b**n <= _M. +// For instance maxPow(10) == (1e19, 19) for 19 decimal digits in a 64bit Word. +// In other words, at most n digits in base b fit into a Word. +// TODO(gri) replace this with a table, generated at build time. +func maxPow(b Word) (p Word, n int) { + p, n = b, 1 // assuming b <= _M + for max := _M / b; p <= max; { + // p == b**n && p <= max + p *= b + n++ + } + // p == b**n && p <= _M + return +} + +// pow returns x**n for n > 0, and 1 otherwise. +func pow(x Word, n int) (p Word) { + // n == sum of bi * 2**i, for 0 <= i < imax, and bi is 0 or 1 + // thus x**n == product of x**(2**i) for all i where bi == 1 + // (Russian Peasant Method for exponentiation) + p = 1 + for n > 0 { + if n&1 != 0 { + p *= x + } + x *= x + n >>= 1 + } + return +} + +// scan scans the number corresponding to the longest possible prefix +// from r representing an unsigned number in a given conversion base. +// It returns the corresponding natural number res, the actual base b, +// a digit count, and a read or syntax error err, if any. +// +// number = [ prefix ] mantissa . +// prefix = "0" [ "x" | "X" | "b" | "B" ] . +// mantissa = digits | digits "." [ digits ] | "." digits . +// digits = digit { digit } . +// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" . +// +// Unless fracOk is set, the base argument must be 0 or a value between +// 2 and MaxBase. If fracOk is set, the base argument must be one of +// 0, 2, 10, or 16. Providing an invalid base argument leads to a run- +// time panic. +// +// For base 0, the number prefix determines the actual base: A prefix of +// ``0x'' or ``0X'' selects base 16; if fracOk is not set, the ``0'' prefix +// selects base 8, and a ``0b'' or ``0B'' prefix selects base 2. Otherwise +// the selected base is 10 and no prefix is accepted. +// +// If fracOk is set, an octal prefix is ignored (a leading ``0'' simply +// stands for a zero digit), and a period followed by a fractional part +// is permitted. The result value is computed as if there were no period +// present; and the count value is used to determine the fractional part. +// +// For bases <= 36, lower and upper case letters are considered the same: +// The letters 'a' to 'z' and 'A' to 'Z' represent digit values 10 to 35. +// For bases > 36, the upper case letters 'A' to 'Z' represent the digit +// values 36 to 61. +// +// A result digit count > 0 corresponds to the number of (non-prefix) digits +// parsed. A digit count <= 0 indicates the presence of a period (if fracOk +// is set, only), and -count is the number of fractional digits found. +// In this case, the actual value of the scanned number is res * b**count. +// +func (z nat) scan(r io.ByteScanner, base int, fracOk bool) (res nat, b, count int, err error) { + // reject illegal bases + baseOk := base == 0 || + !fracOk && 2 <= base && base <= MaxBase || + fracOk && (base == 2 || base == 10 || base == 16) + if !baseOk { + panic(fmt.Sprintf("illegal number base %d", base)) + } + + // one char look-ahead + ch, err := r.ReadByte() + if err != nil { + return + } + + // determine actual base + b = base + if base == 0 { + // actual base is 10 unless there's a base prefix + b = 10 + if ch == '0' { + count = 1 + switch ch, err = r.ReadByte(); err { + case nil: + // possibly one of 0x, 0X, 0b, 0B + if !fracOk { + b = 8 + } + switch ch { + case 'x', 'X': + b = 16 + case 'b', 'B': + b = 2 + } + switch b { + case 16, 2: + count = 0 // prefix is not counted + if ch, err = r.ReadByte(); err != nil { + // io.EOF is also an error in this case + return + } + case 8: + count = 0 // prefix is not counted + } + case io.EOF: + // input is "0" + res = z[:0] + err = nil + return + default: + // read error + return + } + } + } + + // convert string + // Algorithm: Collect digits in groups of at most n digits in di + // and then use mulAddWW for every such group to add them to the + // result. + z = z[:0] + b1 := Word(b) + bn, n := maxPow(b1) // at most n digits in base b1 fit into Word + di := Word(0) // 0 <= di < b1**i < bn + i := 0 // 0 <= i < n + dp := -1 // position of decimal point + for { + if fracOk && ch == '.' { + fracOk = false + dp = count + // advance + if ch, err = r.ReadByte(); err != nil { + if err == io.EOF { + err = nil + break + } + return + } + } + + // convert rune into digit value d1 + var d1 Word + switch { + case '0' <= ch && ch <= '9': + d1 = Word(ch - '0') + case 'a' <= ch && ch <= 'z': + d1 = Word(ch - 'a' + 10) + case 'A' <= ch && ch <= 'Z': + if b <= maxBaseSmall { + d1 = Word(ch - 'A' + 10) + } else { + d1 = Word(ch - 'A' + maxBaseSmall) + } + default: + d1 = MaxBase + 1 + } + if d1 >= b1 { + r.UnreadByte() // ch does not belong to number anymore + break + } + count++ + + // collect d1 in di + di = di*b1 + d1 + i++ + + // if di is "full", add it to the result + if i == n { + z = z.mulAddWW(z, bn, di) + di = 0 + i = 0 + } + + // advance + if ch, err = r.ReadByte(); err != nil { + if err == io.EOF { + err = nil + break + } + return + } + } + + if count == 0 { + // no digits found + switch { + case base == 0 && b == 8: + // there was only the octal prefix 0 (possibly followed by digits > 7); + // count as one digit and return base 10, not 8 + count = 1 + b = 10 + case base != 0 || b != 8: + // there was neither a mantissa digit nor the octal prefix 0 + err = errors.New("syntax error scanning number") + } + return + } + // count > 0 + + // add remaining digits to result + if i > 0 { + z = z.mulAddWW(z, pow(b1, i), di) + } + res = z.norm() + + // adjust for fraction, if any + if dp >= 0 { + // 0 <= dp <= count > 0 + count = dp - count + } + + return +} + +// utoa converts x to an ASCII representation in the given base; +// base must be between 2 and MaxBase, inclusive. +func (x nat) utoa(base int) []byte { + return x.itoa(false, base) +} + +// itoa is like utoa but it prepends a '-' if neg && x != 0. +func (x nat) itoa(neg bool, base int) []byte { + if base < 2 || base > MaxBase { + panic("invalid base") + } + + // x == 0 + if len(x) == 0 { + return []byte("0") + } + // len(x) > 0 + + // allocate buffer for conversion + i := int(float64(x.bitLen())/math.Log2(float64(base))) + 1 // off by 1 at most + if neg { + i++ + } + s := make([]byte, i) + + // convert power of two and non power of two bases separately + if b := Word(base); b == b&-b { + // shift is base b digit size in bits + shift := uint(bits.TrailingZeros(uint(b))) // shift > 0 because b >= 2 + mask := Word(1<= shift { + i-- + s[i] = digits[w&mask] + w >>= shift + nbits -= shift + } + + // convert any partial leading digit and advance to next word + if nbits == 0 { + // no partial digit remaining, just advance + w = x[k] + nbits = _W + } else { + // partial digit in current word w (== x[k-1]) and next word x[k] + w |= x[k] << nbits + i-- + s[i] = digits[w&mask] + + // advance + w = x[k] >> (shift - nbits) + nbits = _W - (shift - nbits) + } + } + + // convert digits of most-significant word w (omit leading zeros) + for w != 0 { + i-- + s[i] = digits[w&mask] + w >>= shift + } + + } else { + bb, ndigits := maxPow(b) + + // construct table of successive squares of bb*leafSize to use in subdivisions + // result (table != nil) <=> (len(x) > leafSize > 0) + table := divisors(len(x), b, ndigits, bb) + + // preserve x, create local copy for use by convertWords + q := nat(nil).set(x) + + // convert q to string s in base b + q.convertWords(s, b, ndigits, bb, table) + + // strip leading zeros + // (x != 0; thus s must contain at least one non-zero digit + // and the loop will terminate) + i = 0 + for s[i] == '0' { + i++ + } + } + + if neg { + i-- + s[i] = '-' + } + + return s[i:] +} + +// Convert words of q to base b digits in s. If q is large, it is recursively "split in half" +// by nat/nat division using tabulated divisors. Otherwise, it is converted iteratively using +// repeated nat/Word division. +// +// The iterative method processes n Words by n divW() calls, each of which visits every Word in the +// incrementally shortened q for a total of n + (n-1) + (n-2) ... + 2 + 1, or n(n+1)/2 divW()'s. +// Recursive conversion divides q by its approximate square root, yielding two parts, each half +// the size of q. Using the iterative method on both halves means 2 * (n/2)(n/2 + 1)/2 divW()'s +// plus the expensive long div(). Asymptotically, the ratio is favorable at 1/2 the divW()'s, and +// is made better by splitting the subblocks recursively. Best is to split blocks until one more +// split would take longer (because of the nat/nat div()) than the twice as many divW()'s of the +// iterative approach. This threshold is represented by leafSize. Benchmarking of leafSize in the +// range 2..64 shows that values of 8 and 16 work well, with a 4x speedup at medium lengths and +// ~30x for 20000 digits. Use nat_test.go's BenchmarkLeafSize tests to optimize leafSize for +// specific hardware. +// +func (q nat) convertWords(s []byte, b Word, ndigits int, bb Word, table []divisor) { + // split larger blocks recursively + if table != nil { + // len(q) > leafSize > 0 + var r nat + index := len(table) - 1 + for len(q) > leafSize { + // find divisor close to sqrt(q) if possible, but in any case < q + maxLength := q.bitLen() // ~= log2 q, or at of least largest possible q of this bit length + minLength := maxLength >> 1 // ~= log2 sqrt(q) + for index > 0 && table[index-1].nbits > minLength { + index-- // desired + } + if table[index].nbits >= maxLength && table[index].bbb.cmp(q) >= 0 { + index-- + if index < 0 { + panic("internal inconsistency") + } + } + + // split q into the two digit number (q'*bbb + r) to form independent subblocks + q, r = q.div(r, q, table[index].bbb) + + // convert subblocks and collect results in s[:h] and s[h:] + h := len(s) - table[index].ndigits + r.convertWords(s[h:], b, ndigits, bb, table[0:index]) + s = s[:h] // == q.convertWords(s, b, ndigits, bb, table[0:index+1]) + } + } + + // having split any large blocks now process the remaining (small) block iteratively + i := len(s) + var r Word + if b == 10 { + // hard-coding for 10 here speeds this up by 1.25x (allows for / and % by constants) + for len(q) > 0 { + // extract least significant, base bb "digit" + q, r = q.divW(q, bb) + for j := 0; j < ndigits && i > 0; j++ { + i-- + // avoid % computation since r%10 == r - int(r/10)*10; + // this appears to be faster for BenchmarkString10000Base10 + // and smaller strings (but a bit slower for larger ones) + t := r / 10 + s[i] = '0' + byte(r-t*10) + r = t + } + } + } else { + for len(q) > 0 { + // extract least significant, base bb "digit" + q, r = q.divW(q, bb) + for j := 0; j < ndigits && i > 0; j++ { + i-- + s[i] = digits[r%b] + r /= b + } + } + } + + // prepend high-order zeros + for i > 0 { // while need more leading zeros + i-- + s[i] = '0' + } +} + +// Split blocks greater than leafSize Words (or set to 0 to disable recursive conversion) +// Benchmark and configure leafSize using: go test -bench="Leaf" +// 8 and 16 effective on 3.0 GHz Xeon "Clovertown" CPU (128 byte cache lines) +// 8 and 16 effective on 2.66 GHz Core 2 Duo "Penryn" CPU +var leafSize int = 8 // number of Word-size binary values treat as a monolithic block + +type divisor struct { + bbb nat // divisor + nbits int // bit length of divisor (discounting leading zeros) ~= log2(bbb) + ndigits int // digit length of divisor in terms of output base digits +} + +var cacheBase10 struct { + sync.Mutex + table [64]divisor // cached divisors for base 10 +} + +// expWW computes x**y +func (z nat) expWW(x, y Word) nat { + return z.expNN(nat(nil).setWord(x), nat(nil).setWord(y), nil) +} + +// construct table of powers of bb*leafSize to use in subdivisions +func divisors(m int, b Word, ndigits int, bb Word) []divisor { + // only compute table when recursive conversion is enabled and x is large + if leafSize == 0 || m <= leafSize { + return nil + } + + // determine k where (bb**leafSize)**(2**k) >= sqrt(x) + k := 1 + for words := leafSize; words < m>>1 && k < len(cacheBase10.table); words <<= 1 { + k++ + } + + // reuse and extend existing table of divisors or create new table as appropriate + var table []divisor // for b == 10, table overlaps with cacheBase10.table + if b == 10 { + cacheBase10.Lock() + table = cacheBase10.table[0:k] // reuse old table for this conversion + } else { + table = make([]divisor, k) // create new table for this conversion + } + + // extend table + if table[k-1].ndigits == 0 { + // add new entries as needed + var larger nat + for i := 0; i < k; i++ { + if table[i].ndigits == 0 { + if i == 0 { + table[0].bbb = nat(nil).expWW(bb, Word(leafSize)) + table[0].ndigits = ndigits * leafSize + } else { + table[i].bbb = nat(nil).sqr(table[i-1].bbb) + table[i].ndigits = 2 * table[i-1].ndigits + } + + // optimization: exploit aggregated extra bits in macro blocks + larger = nat(nil).set(table[i].bbb) + for mulAddVWW(larger, larger, b, 0) == 0 { + table[i].bbb = table[i].bbb.set(larger) + table[i].ndigits++ + } + + table[i].nbits = table[i].bbb.bitLen() + } + } + } + + if b == 10 { + cacheBase10.Unlock() + } + + return table +} diff --git a/vendor/github.com/golang/go/src/math/big/prime.go b/vendor/github.com/golang/go/src/math/big/prime.go new file mode 100644 index 000000000000..848affbf5bf4 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/prime.go @@ -0,0 +1,320 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package big + +import "math/rand" + +// ProbablyPrime reports whether x is probably prime, +// applying the Miller-Rabin test with n pseudorandomly chosen bases +// as well as a Baillie-PSW test. +// +// If x is prime, ProbablyPrime returns true. +// If x is chosen randomly and not prime, ProbablyPrime probably returns false. +// The probability of returning true for a randomly chosen non-prime is at most ¼ⁿ. +// +// ProbablyPrime is 100% accurate for inputs less than 2⁶⁴. +// See Menezes et al., Handbook of Applied Cryptography, 1997, pp. 145-149, +// and FIPS 186-4 Appendix F for further discussion of the error probabilities. +// +// ProbablyPrime is not suitable for judging primes that an adversary may +// have crafted to fool the test. +// +// As of Go 1.8, ProbablyPrime(0) is allowed and applies only a Baillie-PSW test. +// Before Go 1.8, ProbablyPrime applied only the Miller-Rabin tests, and ProbablyPrime(0) panicked. +func (x *Int) ProbablyPrime(n int) bool { + // Note regarding the doc comment above: + // It would be more precise to say that the Baillie-PSW test uses the + // extra strong Lucas test as its Lucas test, but since no one knows + // how to tell any of the Lucas tests apart inside a Baillie-PSW test + // (they all work equally well empirically), that detail need not be + // documented or implicitly guaranteed. + // The comment does avoid saying "the" Baillie-PSW test + // because of this general ambiguity. + + if n < 0 { + panic("negative n for ProbablyPrime") + } + if x.neg || len(x.abs) == 0 { + return false + } + + // primeBitMask records the primes < 64. + const primeBitMask uint64 = 1<<2 | 1<<3 | 1<<5 | 1<<7 | + 1<<11 | 1<<13 | 1<<17 | 1<<19 | 1<<23 | 1<<29 | 1<<31 | + 1<<37 | 1<<41 | 1<<43 | 1<<47 | 1<<53 | 1<<59 | 1<<61 + + w := x.abs[0] + if len(x.abs) == 1 && w < 64 { + return primeBitMask&(1< 10000 { + // This is widely believed to be impossible. + // If we get a report, we'll want the exact number n. + panic("math/big: internal error: cannot find (D/n) = -1 for " + intN.String()) + } + d[0] = p*p - 4 + j := Jacobi(intD, intN) + if j == -1 { + break + } + if j == 0 { + // d = p²-4 = (p-2)(p+2). + // If (d/n) == 0 then d shares a prime factor with n. + // Since the loop proceeds in increasing p and starts with p-2==1, + // the shared prime factor must be p+2. + // If p+2 == n, then n is prime; otherwise p+2 is a proper factor of n. + return len(n) == 1 && n[0] == p+2 + } + if p == 40 { + // We'll never find (d/n) = -1 if n is a square. + // If n is a non-square we expect to find a d in just a few attempts on average. + // After 40 attempts, take a moment to check if n is indeed a square. + t1 = t1.sqrt(n) + t1 = t1.sqr(t1) + if t1.cmp(n) == 0 { + return false + } + } + } + + // Grantham definition of "extra strong Lucas pseudoprime", after Thm 2.3 on p. 876 + // (D, P, Q above have become Δ, b, 1): + // + // Let U_n = U_n(b, 1), V_n = V_n(b, 1), and Δ = b²-4. + // An extra strong Lucas pseudoprime to base b is a composite n = 2^r s + Jacobi(Δ, n), + // where s is odd and gcd(n, 2*Δ) = 1, such that either (i) U_s ≡ 0 mod n and V_s ≡ ±2 mod n, + // or (ii) V_{2^t s} ≡ 0 mod n for some 0 ≤ t < r-1. + // + // We know gcd(n, Δ) = 1 or else we'd have found Jacobi(d, n) == 0 above. + // We know gcd(n, 2) = 1 because n is odd. + // + // Arrange s = (n - Jacobi(Δ, n)) / 2^r = (n+1) / 2^r. + s := nat(nil).add(n, natOne) + r := int(s.trailingZeroBits()) + s = s.shr(s, uint(r)) + nm2 := nat(nil).sub(n, natTwo) // n-2 + + // We apply the "almost extra strong" test, which checks the above conditions + // except for U_s ≡ 0 mod n, which allows us to avoid computing any U_k values. + // Jacobsen points out that maybe we should just do the full extra strong test: + // "It is also possible to recover U_n using Crandall and Pomerance equation 3.13: + // U_n = D^-1 (2V_{n+1} - PV_n) allowing us to run the full extra-strong test + // at the cost of a single modular inversion. This computation is easy and fast in GMP, + // so we can get the full extra-strong test at essentially the same performance as the + // almost extra strong test." + + // Compute Lucas sequence V_s(b, 1), where: + // + // V(0) = 2 + // V(1) = P + // V(k) = P V(k-1) - Q V(k-2). + // + // (Remember that due to method C above, P = b, Q = 1.) + // + // In general V(k) = α^k + β^k, where α and β are roots of x² - Px + Q. + // Crandall and Pomerance (p.147) observe that for 0 ≤ j ≤ k, + // + // V(j+k) = V(j)V(k) - V(k-j). + // + // So in particular, to quickly double the subscript: + // + // V(2k) = V(k)² - 2 + // V(2k+1) = V(k) V(k+1) - P + // + // We can therefore start with k=0 and build up to k=s in log₂(s) steps. + natP := nat(nil).setWord(p) + vk := nat(nil).setWord(2) + vk1 := nat(nil).setWord(p) + t2 := nat(nil) // temp + for i := int(s.bitLen()); i >= 0; i-- { + if s.bit(uint(i)) != 0 { + // k' = 2k+1 + // V(k') = V(2k+1) = V(k) V(k+1) - P. + t1 = t1.mul(vk, vk1) + t1 = t1.add(t1, n) + t1 = t1.sub(t1, natP) + t2, vk = t2.div(vk, t1, n) + // V(k'+1) = V(2k+2) = V(k+1)² - 2. + t1 = t1.sqr(vk1) + t1 = t1.add(t1, nm2) + t2, vk1 = t2.div(vk1, t1, n) + } else { + // k' = 2k + // V(k'+1) = V(2k+1) = V(k) V(k+1) - P. + t1 = t1.mul(vk, vk1) + t1 = t1.add(t1, n) + t1 = t1.sub(t1, natP) + t2, vk1 = t2.div(vk1, t1, n) + // V(k') = V(2k) = V(k)² - 2 + t1 = t1.sqr(vk) + t1 = t1.add(t1, nm2) + t2, vk = t2.div(vk, t1, n) + } + } + + // Now k=s, so vk = V(s). Check V(s) ≡ ±2 (mod n). + if vk.cmp(natTwo) == 0 || vk.cmp(nm2) == 0 { + // Check U(s) ≡ 0. + // As suggested by Jacobsen, apply Crandall and Pomerance equation 3.13: + // + // U(k) = D⁻¹ (2 V(k+1) - P V(k)) + // + // Since we are checking for U(k) == 0 it suffices to check 2 V(k+1) == P V(k) mod n, + // or P V(k) - 2 V(k+1) == 0 mod n. + t1 := t1.mul(vk, natP) + t2 := t2.shl(vk1, 1) + if t1.cmp(t2) < 0 { + t1, t2 = t2, t1 + } + t1 = t1.sub(t1, t2) + t3 := vk1 // steal vk1, no longer needed below + vk1 = nil + _ = vk1 + t2, t3 = t2.div(t3, t1, n) + if len(t3) == 0 { + return true + } + } + + // Check V(2^t s) ≡ 0 mod n for some 0 ≤ t < r-1. + for t := 0; t < r-1; t++ { + if len(vk) == 0 { // vk == 0 + return true + } + // Optimization: V(k) = 2 is a fixed point for V(k') = V(k)² - 2, + // so if V(k) = 2, we can stop: we will never find a future V(k) == 0. + if len(vk) == 1 && vk[0] == 2 { // vk == 2 + return false + } + // k' = 2k + // V(k') = V(2k) = V(k)² - 2 + t1 = t1.sqr(vk) + t1 = t1.sub(t1, natTwo) + t2, vk = t2.div(vk, t1, n) + } + return false +} diff --git a/vendor/github.com/golang/go/src/math/big/rat.go b/vendor/github.com/golang/go/src/math/big/rat.go new file mode 100644 index 000000000000..b33fc696adf5 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/rat.go @@ -0,0 +1,517 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements multi-precision rational numbers. + +package big + +import ( + "fmt" + "math" +) + +// A Rat represents a quotient a/b of arbitrary precision. +// The zero value for a Rat represents the value 0. +type Rat struct { + // To make zero values for Rat work w/o initialization, + // a zero value of b (len(b) == 0) acts like b == 1. + // a.neg determines the sign of the Rat, b.neg is ignored. + a, b Int +} + +// NewRat creates a new Rat with numerator a and denominator b. +func NewRat(a, b int64) *Rat { + return new(Rat).SetFrac64(a, b) +} + +// SetFloat64 sets z to exactly f and returns z. +// If f is not finite, SetFloat returns nil. +func (z *Rat) SetFloat64(f float64) *Rat { + const expMask = 1<<11 - 1 + bits := math.Float64bits(f) + mantissa := bits & (1<<52 - 1) + exp := int((bits >> 52) & expMask) + switch exp { + case expMask: // non-finite + return nil + case 0: // denormal + exp -= 1022 + default: // normal + mantissa |= 1 << 52 + exp -= 1023 + } + + shift := 52 - exp + + // Optimization (?): partially pre-normalise. + for mantissa&1 == 0 && shift > 0 { + mantissa >>= 1 + shift-- + } + + z.a.SetUint64(mantissa) + z.a.neg = f < 0 + z.b.Set(intOne) + if shift > 0 { + z.b.Lsh(&z.b, uint(shift)) + } else { + z.a.Lsh(&z.a, uint(-shift)) + } + return z.norm() +} + +// quotToFloat32 returns the non-negative float32 value +// nearest to the quotient a/b, using round-to-even in +// halfway cases. It does not mutate its arguments. +// Preconditions: b is non-zero; a and b have no common factors. +func quotToFloat32(a, b nat) (f float32, exact bool) { + const ( + // float size in bits + Fsize = 32 + + // mantissa + Msize = 23 + Msize1 = Msize + 1 // incl. implicit 1 + Msize2 = Msize1 + 1 + + // exponent + Esize = Fsize - Msize1 + Ebias = 1<<(Esize-1) - 1 + Emin = 1 - Ebias + Emax = Ebias + ) + + // TODO(adonovan): specialize common degenerate cases: 1.0, integers. + alen := a.bitLen() + if alen == 0 { + return 0, true + } + blen := b.bitLen() + if blen == 0 { + panic("division by zero") + } + + // 1. Left-shift A or B such that quotient A/B is in [1<= B). + // This is 2 or 3 more than the float32 mantissa field width of Msize: + // - the optional extra bit is shifted away in step 3 below. + // - the high-order 1 is omitted in "normal" representation; + // - the low-order 1 will be used during rounding then discarded. + exp := alen - blen + var a2, b2 nat + a2 = a2.set(a) + b2 = b2.set(b) + if shift := Msize2 - exp; shift > 0 { + a2 = a2.shl(a2, uint(shift)) + } else if shift < 0 { + b2 = b2.shl(b2, uint(-shift)) + } + + // 2. Compute quotient and remainder (q, r). NB: due to the + // extra shift, the low-order bit of q is logically the + // high-order bit of r. + var q nat + q, r := q.div(a2, a2, b2) // (recycle a2) + mantissa := low32(q) + haveRem := len(r) > 0 // mantissa&1 && !haveRem => remainder is exactly half + + // 3. If quotient didn't fit in Msize2 bits, redo division by b2<<1 + // (in effect---we accomplish this incrementally). + if mantissa>>Msize2 == 1 { + if mantissa&1 == 1 { + haveRem = true + } + mantissa >>= 1 + exp++ + } + if mantissa>>Msize1 != 1 { + panic(fmt.Sprintf("expected exactly %d bits of result", Msize2)) + } + + // 4. Rounding. + if Emin-Msize <= exp && exp <= Emin { + // Denormal case; lose 'shift' bits of precision. + shift := uint(Emin - (exp - 1)) // [1..Esize1) + lostbits := mantissa & (1<>= shift + exp = 2 - Ebias // == exp + shift + } + // Round q using round-half-to-even. + exact = !haveRem + if mantissa&1 != 0 { + exact = false + if haveRem || mantissa&2 != 0 { + if mantissa++; mantissa >= 1< 100...0, so shift is safe + mantissa >>= 1 + exp++ + } + } + } + mantissa >>= 1 // discard rounding bit. Mantissa now scaled by 1<= B). + // This is 2 or 3 more than the float64 mantissa field width of Msize: + // - the optional extra bit is shifted away in step 3 below. + // - the high-order 1 is omitted in "normal" representation; + // - the low-order 1 will be used during rounding then discarded. + exp := alen - blen + var a2, b2 nat + a2 = a2.set(a) + b2 = b2.set(b) + if shift := Msize2 - exp; shift > 0 { + a2 = a2.shl(a2, uint(shift)) + } else if shift < 0 { + b2 = b2.shl(b2, uint(-shift)) + } + + // 2. Compute quotient and remainder (q, r). NB: due to the + // extra shift, the low-order bit of q is logically the + // high-order bit of r. + var q nat + q, r := q.div(a2, a2, b2) // (recycle a2) + mantissa := low64(q) + haveRem := len(r) > 0 // mantissa&1 && !haveRem => remainder is exactly half + + // 3. If quotient didn't fit in Msize2 bits, redo division by b2<<1 + // (in effect---we accomplish this incrementally). + if mantissa>>Msize2 == 1 { + if mantissa&1 == 1 { + haveRem = true + } + mantissa >>= 1 + exp++ + } + if mantissa>>Msize1 != 1 { + panic(fmt.Sprintf("expected exactly %d bits of result", Msize2)) + } + + // 4. Rounding. + if Emin-Msize <= exp && exp <= Emin { + // Denormal case; lose 'shift' bits of precision. + shift := uint(Emin - (exp - 1)) // [1..Esize1) + lostbits := mantissa & (1<>= shift + exp = 2 - Ebias // == exp + shift + } + // Round q using round-half-to-even. + exact = !haveRem + if mantissa&1 != 0 { + exact = false + if haveRem || mantissa&2 != 0 { + if mantissa++; mantissa >= 1< 100...0, so shift is safe + mantissa >>= 1 + exp++ + } + } + } + mantissa >>= 1 // discard rounding bit. Mantissa now scaled by 1< 0 && !z.a.neg // 0 has no sign + return z +} + +// Inv sets z to 1/x and returns z. +func (z *Rat) Inv(x *Rat) *Rat { + if len(x.a.abs) == 0 { + panic("division by zero") + } + z.Set(x) + a := z.b.abs + if len(a) == 0 { + a = a.set(natOne) // materialize numerator + } + b := z.a.abs + if b.cmp(natOne) == 0 { + b = b[:0] // normalize denominator + } + z.a.abs, z.b.abs = a, b // sign doesn't change + return z +} + +// Sign returns: +// +// -1 if x < 0 +// 0 if x == 0 +// +1 if x > 0 +// +func (x *Rat) Sign() int { + return x.a.Sign() +} + +// IsInt reports whether the denominator of x is 1. +func (x *Rat) IsInt() bool { + return len(x.b.abs) == 0 || x.b.abs.cmp(natOne) == 0 +} + +// Num returns the numerator of x; it may be <= 0. +// The result is a reference to x's numerator; it +// may change if a new value is assigned to x, and vice versa. +// The sign of the numerator corresponds to the sign of x. +func (x *Rat) Num() *Int { + return &x.a +} + +// Denom returns the denominator of x; it is always > 0. +// The result is a reference to x's denominator; it +// may change if a new value is assigned to x, and vice versa. +func (x *Rat) Denom() *Int { + x.b.neg = false // the result is always >= 0 + if len(x.b.abs) == 0 { + x.b.abs = x.b.abs.set(natOne) // materialize denominator + } + return &x.b +} + +func (z *Rat) norm() *Rat { + switch { + case len(z.a.abs) == 0: + // z == 0 - normalize sign and denominator + z.a.neg = false + z.b.abs = z.b.abs[:0] + case len(z.b.abs) == 0: + // z is normalized int - nothing to do + case z.b.abs.cmp(natOne) == 0: + // z is int - normalize denominator + z.b.abs = z.b.abs[:0] + default: + neg := z.a.neg + z.a.neg = false + z.b.neg = false + if f := NewInt(0).lehmerGCD(&z.a, &z.b); f.Cmp(intOne) != 0 { + z.a.abs, _ = z.a.abs.div(nil, z.a.abs, f.abs) + z.b.abs, _ = z.b.abs.div(nil, z.b.abs, f.abs) + if z.b.abs.cmp(natOne) == 0 { + // z is int - normalize denominator + z.b.abs = z.b.abs[:0] + } + } + z.a.neg = neg + } + return z +} + +// mulDenom sets z to the denominator product x*y (by taking into +// account that 0 values for x or y must be interpreted as 1) and +// returns z. +func mulDenom(z, x, y nat) nat { + switch { + case len(x) == 0: + return z.set(y) + case len(y) == 0: + return z.set(x) + } + return z.mul(x, y) +} + +// scaleDenom computes x*f. +// If f == 0 (zero value of denominator), the result is (a copy of) x. +func scaleDenom(x *Int, f nat) *Int { + var z Int + if len(f) == 0 { + return z.Set(x) + } + z.abs = z.abs.mul(x.abs, f) + z.neg = x.neg + return &z +} + +// Cmp compares x and y and returns: +// +// -1 if x < y +// 0 if x == y +// +1 if x > y +// +func (x *Rat) Cmp(y *Rat) int { + return scaleDenom(&x.a, y.b.abs).Cmp(scaleDenom(&y.a, x.b.abs)) +} + +// Add sets z to the sum x+y and returns z. +func (z *Rat) Add(x, y *Rat) *Rat { + a1 := scaleDenom(&x.a, y.b.abs) + a2 := scaleDenom(&y.a, x.b.abs) + z.a.Add(a1, a2) + z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs) + return z.norm() +} + +// Sub sets z to the difference x-y and returns z. +func (z *Rat) Sub(x, y *Rat) *Rat { + a1 := scaleDenom(&x.a, y.b.abs) + a2 := scaleDenom(&y.a, x.b.abs) + z.a.Sub(a1, a2) + z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs) + return z.norm() +} + +// Mul sets z to the product x*y and returns z. +func (z *Rat) Mul(x, y *Rat) *Rat { + if x == y { + // a squared Rat is positive and can't be reduced + z.a.neg = false + z.a.abs = z.a.abs.sqr(x.a.abs) + z.b.abs = z.b.abs.sqr(x.b.abs) + return z + } + z.a.Mul(&x.a, &y.a) + z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs) + return z.norm() +} + +// Quo sets z to the quotient x/y and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +func (z *Rat) Quo(x, y *Rat) *Rat { + if len(y.a.abs) == 0 { + panic("division by zero") + } + a := scaleDenom(&x.a, y.b.abs) + b := scaleDenom(&y.a, x.b.abs) + z.a.abs = a.abs + z.b.abs = b.abs + z.a.neg = a.neg != b.neg + return z.norm() +} diff --git a/vendor/github.com/golang/go/src/math/big/ratconv.go b/vendor/github.com/golang/go/src/math/big/ratconv.go new file mode 100644 index 000000000000..157d8d006d4f --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/ratconv.go @@ -0,0 +1,283 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements rat-to-string conversion functions. + +package big + +import ( + "errors" + "fmt" + "io" + "strconv" + "strings" +) + +func ratTok(ch rune) bool { + return strings.ContainsRune("+-/0123456789.eE", ch) +} + +var ratZero Rat +var _ fmt.Scanner = &ratZero // *Rat must implement fmt.Scanner + +// Scan is a support routine for fmt.Scanner. It accepts the formats +// 'e', 'E', 'f', 'F', 'g', 'G', and 'v'. All formats are equivalent. +func (z *Rat) Scan(s fmt.ScanState, ch rune) error { + tok, err := s.Token(true, ratTok) + if err != nil { + return err + } + if !strings.ContainsRune("efgEFGv", ch) { + return errors.New("Rat.Scan: invalid verb") + } + if _, ok := z.SetString(string(tok)); !ok { + return errors.New("Rat.Scan: invalid syntax") + } + return nil +} + +// SetString sets z to the value of s and returns z and a boolean indicating +// success. s can be given as a fraction "a/b" or as a floating-point number +// optionally followed by an exponent. The entire string (not just a prefix) +// must be valid for success. If the operation failed, the value of z is +// undefined but the returned value is nil. +func (z *Rat) SetString(s string) (*Rat, bool) { + if len(s) == 0 { + return nil, false + } + // len(s) > 0 + + // parse fraction a/b, if any + if sep := strings.Index(s, "/"); sep >= 0 { + if _, ok := z.a.SetString(s[:sep], 0); !ok { + return nil, false + } + r := strings.NewReader(s[sep+1:]) + var err error + if z.b.abs, _, _, err = z.b.abs.scan(r, 0, false); err != nil { + return nil, false + } + // entire string must have been consumed + if _, err = r.ReadByte(); err != io.EOF { + return nil, false + } + if len(z.b.abs) == 0 { + return nil, false + } + return z.norm(), true + } + + // parse floating-point number + r := strings.NewReader(s) + + // sign + neg, err := scanSign(r) + if err != nil { + return nil, false + } + + // mantissa + var ecorr int + z.a.abs, _, ecorr, err = z.a.abs.scan(r, 10, true) + if err != nil { + return nil, false + } + + // exponent + var exp int64 + exp, _, err = scanExponent(r, false) + if err != nil { + return nil, false + } + + // there should be no unread characters left + if _, err = r.ReadByte(); err != io.EOF { + return nil, false + } + + // special-case 0 (see also issue #16176) + if len(z.a.abs) == 0 { + return z, true + } + // len(z.a.abs) > 0 + + // correct exponent + if ecorr < 0 { + exp += int64(ecorr) + } + + // compute exponent power + expabs := exp + if expabs < 0 { + expabs = -expabs + } + powTen := nat(nil).expNN(natTen, nat(nil).setWord(Word(expabs)), nil) + + // complete fraction + if exp < 0 { + z.b.abs = powTen + z.norm() + } else { + z.a.abs = z.a.abs.mul(z.a.abs, powTen) + z.b.abs = z.b.abs[:0] + } + + z.a.neg = neg && len(z.a.abs) > 0 // 0 has no sign + + return z, true +} + +// scanExponent scans the longest possible prefix of r representing a decimal +// ('e', 'E') or binary ('p') exponent, if any. It returns the exponent, the +// exponent base (10 or 2), or a read or syntax error, if any. +// +// exponent = ( "E" | "e" | "p" ) [ sign ] digits . +// sign = "+" | "-" . +// digits = digit { digit } . +// digit = "0" ... "9" . +// +// A binary exponent is only permitted if binExpOk is set. +func scanExponent(r io.ByteScanner, binExpOk bool) (exp int64, base int, err error) { + base = 10 + + var ch byte + if ch, err = r.ReadByte(); err != nil { + if err == io.EOF { + err = nil // no exponent; same as e0 + } + return + } + + switch ch { + case 'e', 'E': + // ok + case 'p': + if binExpOk { + base = 2 + break // ok + } + fallthrough // binary exponent not permitted + default: + r.UnreadByte() + return // no exponent; same as e0 + } + + var neg bool + if neg, err = scanSign(r); err != nil { + return + } + + var digits []byte + if neg { + digits = append(digits, '-') + } + + // no need to use nat.scan for exponent digits + // since we only care about int64 values - the + // from-scratch scan is easy enough and faster + for i := 0; ; i++ { + if ch, err = r.ReadByte(); err != nil { + if err != io.EOF || i == 0 { + return + } + err = nil + break // i > 0 + } + if ch < '0' || '9' < ch { + if i == 0 { + r.UnreadByte() + err = fmt.Errorf("invalid exponent (missing digits)") + return + } + break // i > 0 + } + digits = append(digits, ch) + } + // i > 0 => we have at least one digit + + exp, err = strconv.ParseInt(string(digits), 10, 64) + return +} + +// String returns a string representation of x in the form "a/b" (even if b == 1). +func (x *Rat) String() string { + return string(x.marshal()) +} + +// marshal implements String returning a slice of bytes +func (x *Rat) marshal() []byte { + var buf []byte + buf = x.a.Append(buf, 10) + buf = append(buf, '/') + if len(x.b.abs) != 0 { + buf = x.b.Append(buf, 10) + } else { + buf = append(buf, '1') + } + return buf +} + +// RatString returns a string representation of x in the form "a/b" if b != 1, +// and in the form "a" if b == 1. +func (x *Rat) RatString() string { + if x.IsInt() { + return x.a.String() + } + return x.String() +} + +// FloatString returns a string representation of x in decimal form with prec +// digits of precision after the decimal point. The last digit is rounded to +// nearest, with halves rounded away from zero. +func (x *Rat) FloatString(prec int) string { + var buf []byte + + if x.IsInt() { + buf = x.a.Append(buf, 10) + if prec > 0 { + buf = append(buf, '.') + for i := prec; i > 0; i-- { + buf = append(buf, '0') + } + } + return string(buf) + } + // x.b.abs != 0 + + q, r := nat(nil).div(nat(nil), x.a.abs, x.b.abs) + + p := natOne + if prec > 0 { + p = nat(nil).expNN(natTen, nat(nil).setUint64(uint64(prec)), nil) + } + + r = r.mul(r, p) + r, r2 := r.div(nat(nil), r, x.b.abs) + + // see if we need to round up + r2 = r2.add(r2, r2) + if x.b.abs.cmp(r2) <= 0 { + r = r.add(r, natOne) + if r.cmp(p) >= 0 { + q = nat(nil).add(q, natOne) + r = nat(nil).sub(r, p) + } + } + + if x.a.neg { + buf = append(buf, '-') + } + buf = append(buf, q.utoa(10)...) // itoa ignores sign if q == 0 + + if prec > 0 { + buf = append(buf, '.') + rs := r.utoa(10) + for i := prec - len(rs); i > 0; i-- { + buf = append(buf, '0') + } + buf = append(buf, rs...) + } + + return string(buf) +} diff --git a/vendor/github.com/golang/go/src/math/big/ratmarsh.go b/vendor/github.com/golang/go/src/math/big/ratmarsh.go new file mode 100644 index 000000000000..fbc7b6002d95 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/ratmarsh.go @@ -0,0 +1,75 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements encoding/decoding of Rats. + +package big + +import ( + "encoding/binary" + "errors" + "fmt" +) + +// Gob codec version. Permits backward-compatible changes to the encoding. +const ratGobVersion byte = 1 + +// GobEncode implements the gob.GobEncoder interface. +func (x *Rat) GobEncode() ([]byte, error) { + if x == nil { + return nil, nil + } + buf := make([]byte, 1+4+(len(x.a.abs)+len(x.b.abs))*_S) // extra bytes for version and sign bit (1), and numerator length (4) + i := x.b.abs.bytes(buf) + j := x.a.abs.bytes(buf[:i]) + n := i - j + if int(uint32(n)) != n { + // this should never happen + return nil, errors.New("Rat.GobEncode: numerator too large") + } + binary.BigEndian.PutUint32(buf[j-4:j], uint32(n)) + j -= 1 + 4 + b := ratGobVersion << 1 // make space for sign bit + if x.a.neg { + b |= 1 + } + buf[j] = b + return buf[j:], nil +} + +// GobDecode implements the gob.GobDecoder interface. +func (z *Rat) GobDecode(buf []byte) error { + if len(buf) == 0 { + // Other side sent a nil or default value. + *z = Rat{} + return nil + } + b := buf[0] + if b>>1 != ratGobVersion { + return fmt.Errorf("Rat.GobDecode: encoding version %d not supported", b>>1) + } + const j = 1 + 4 + i := j + binary.BigEndian.Uint32(buf[j-4:j]) + z.a.neg = b&1 != 0 + z.a.abs = z.a.abs.setBytes(buf[j:i]) + z.b.abs = z.b.abs.setBytes(buf[i:]) + return nil +} + +// MarshalText implements the encoding.TextMarshaler interface. +func (x *Rat) MarshalText() (text []byte, err error) { + if x.IsInt() { + return x.a.MarshalText() + } + return x.marshal(), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (z *Rat) UnmarshalText(text []byte) error { + // TODO(gri): get rid of the []byte/string conversion + if _, ok := z.SetString(string(text)); !ok { + return fmt.Errorf("math/big: cannot unmarshal %q into a *big.Rat", text) + } + return nil +} diff --git a/vendor/github.com/golang/go/src/math/big/roundingmode_string.go b/vendor/github.com/golang/go/src/math/big/roundingmode_string.go new file mode 100644 index 000000000000..05024b806562 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/roundingmode_string.go @@ -0,0 +1,16 @@ +// generated by stringer -type=RoundingMode; DO NOT EDIT + +package big + +import "fmt" + +const _RoundingMode_name = "ToNearestEvenToNearestAwayToZeroAwayFromZeroToNegativeInfToPositiveInf" + +var _RoundingMode_index = [...]uint8{0, 13, 26, 32, 44, 57, 70} + +func (i RoundingMode) String() string { + if i+1 >= RoundingMode(len(_RoundingMode_index)) { + return fmt.Sprintf("RoundingMode(%d)", i) + } + return _RoundingMode_name[_RoundingMode_index[i]:_RoundingMode_index[i+1]] +} diff --git a/vendor/github.com/golang/go/src/math/big/sqrt.go b/vendor/github.com/golang/go/src/math/big/sqrt.go new file mode 100644 index 000000000000..00433cfe7a70 --- /dev/null +++ b/vendor/github.com/golang/go/src/math/big/sqrt.go @@ -0,0 +1,151 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package big + +import "math" + +var ( + half = NewFloat(0.5) + two = NewFloat(2.0) + three = NewFloat(3.0) +) + +// Sqrt sets z to the rounded square root of x, and returns it. +// +// If z's precision is 0, it is changed to x's precision before the +// operation. Rounding is performed according to z's precision and +// rounding mode. +// +// The function panics if z < 0. The value of z is undefined in that +// case. +func (z *Float) Sqrt(x *Float) *Float { + if debugFloat { + x.validate() + } + + if z.prec == 0 { + z.prec = x.prec + } + + if x.Sign() == -1 { + // following IEEE754-2008 (section 7.2) + panic(ErrNaN{"square root of negative operand"}) + } + + // handle ±0 and +∞ + if x.form != finite { + z.acc = Exact + z.form = x.form + z.neg = x.neg // IEEE754-2008 requires √±0 = ±0 + return z + } + + // MantExp sets the argument's precision to the receiver's, and + // when z.prec > x.prec this will lower z.prec. Restore it after + // the MantExp call. + prec := z.prec + b := x.MantExp(z) + z.prec = prec + + // Compute √(z·2**b) as + // √( z)·2**(½b) if b is even + // √(2z)·2**(⌊½b⌋) if b > 0 is odd + // √(½z)·2**(⌈½b⌉) if b < 0 is odd + switch b % 2 { + case 0: + // nothing to do + case 1: + z.Mul(two, z) + case -1: + z.Mul(half, z) + } + // 0.25 <= z < 2.0 + + // Solving x² - z = 0 directly requires a Quo call, but it's + // faster for small precisions. + // + // Solving 1/x² - z = 0 avoids the Quo call and is much faster for + // high precisions. + // + // 128bit precision is an empirically chosen threshold. + if z.prec <= 128 { + z.sqrtDirect(z) + } else { + z.sqrtInverse(z) + } + + // re-attach halved exponent + return z.SetMantExp(z, b/2) +} + +// Compute √x (up to prec 128) by solving +// t² - x = 0 +// for t, starting with a 53 bits precision guess from math.Sqrt and +// then using at most two iterations of Newton's method. +func (z *Float) sqrtDirect(x *Float) { + // let + // f(t) = t² - x + // then + // g(t) = f(t)/f'(t) = ½(t² - x)/t + // and the next guess is given by + // t2 = t - g(t) = ½(t² + x)/t + u := new(Float) + ng := func(t *Float) *Float { + u.prec = t.prec + u.Mul(t, t) // u = t² + u.Add(u, x) // = t² + x + u.Mul(half, u) // = ½(t² + x) + return t.Quo(u, t) // = ½(t² + x)/t + } + + xf, _ := x.Float64() + sq := NewFloat(math.Sqrt(xf)) + + switch { + case z.prec > 128: + panic("sqrtDirect: only for z.prec <= 128") + case z.prec > 64: + sq.prec *= 2 + sq = ng(sq) + fallthrough + default: + sq.prec *= 2 + sq = ng(sq) + } + + z.Set(sq) +} + +// Compute √x (to z.prec precision) by solving +// 1/t² - x = 0 +// for t (using Newton's method), and then inverting. +func (z *Float) sqrtInverse(x *Float) { + // let + // f(t) = 1/t² - x + // then + // g(t) = f(t)/f'(t) = -½t(1 - xt²) + // and the next guess is given by + // t2 = t - g(t) = ½t(3 - xt²) + u := new(Float) + ng := func(t *Float) *Float { + u.prec = t.prec + u.Mul(t, t) // u = t² + u.Mul(x, u) // = xt² + u.Sub(three, u) // = 3 - xt² + u.Mul(t, u) // = t(3 - xt²) + return t.Mul(half, u) // = ½t(3 - xt²) + } + + xf, _ := x.Float64() + sqi := NewFloat(1 / math.Sqrt(xf)) + for prec := z.prec + 32; sqi.prec < prec; { + sqi.prec *= 2 + sqi = ng(sqi) + } + // sqi = 1/√x + + // x/√x = √x + z.Mul(x, sqi) +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 84f64d244af1..ea384f11ae04 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -924,6 +924,12 @@ "revision": "23def4e6c14b4da8ac2ed8007337bc5eb5007998", "revisionTime": "2016-01-25T20:49:56Z" }, + { + "checksumSHA1": "y1GG7DFXzx8ABfTWlVdgMVvlYU0=", + "path": "github.com/golang/go/src/math/big", + "revision": "bf86aec25972f3a100c3aa58a6abcbcc35bdea49", + "revisionTime": "2018-02-16T04:20:11Z" + }, { "checksumSHA1": "WX1+2gktHcBmE9MGwFSGs7oqexU=", "path": "github.com/golang/protobuf/proto", From 32b48f73be2b4e68c3ccf179e6b793b5f0d3e0f6 Mon Sep 17 00:00:00 2001 From: Brian Kassouf Date: Thu, 8 Mar 2018 21:06:25 -0800 Subject: [PATCH 19/45] helper/keyutil: Add a NewPolicy function so unexported variables can be set (#4113) * helper/keyutil: Add a NewPolicy function so unexported variables can be set * Set the convergent version --- helper/keysutil/policy.go | 59 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/helper/keysutil/policy.go b/helper/keysutil/policy.go index 503d11079fc5..b13ebd5938b7 100644 --- a/helper/keysutil/policy.go +++ b/helper/keysutil/policy.go @@ -205,6 +205,63 @@ func (kem deprecatedKeyEntryMap) UnmarshalJSON(data []byte) error { // keyEntryMap is used to allow JSON marshal/unmarshal type keyEntryMap map[string]KeyEntry +// PolicyConfig is used to create a new policy +type PolicyConfig struct { + // The name of the policy + Name string `json:"name"` + + // The type of key + Type KeyType + + // Derived keys MUST provide a context and the master underlying key is + // never used. If convergent encryption is true, the context will be used + // as the nonce as well. + Derived bool + KDF int + ConvergentEncryption bool + + // Whether the key is exportable + Exportable bool + + // Whether the key is allowed to be deleted + DeletionAllowed bool + + // AllowPlaintextBackup allows taking backup of the policy in plaintext + AllowPlaintextBackup bool + + // VersionTemplate is used to prefix the ciphertext with information about + // the key version. It must inclide {{version}} and a delimiter between the + // version prefix and the ciphertext. + VersionTemplate string + + // StoragePrefix is used to add a prefix when storing and retrieving the + // policy object. + StoragePrefix string +} + +// NewPolicy takes a policy config and returns a Policy with those settings. +func NewPolicy(config PolicyConfig) *Policy { + var convergentVersion int + if config.ConvergentEncryption { + convergentVersion = 2 + } + + return &Policy{ + Name: config.Name, + Type: config.Type, + Derived: config.Derived, + KDF: config.KDF, + ConvergentEncryption: config.ConvergentEncryption, + ConvergentVersion: convergentVersion, + Exportable: config.Exportable, + DeletionAllowed: config.DeletionAllowed, + AllowPlaintextBackup: config.AllowPlaintextBackup, + VersionTemplate: config.VersionTemplate, + StoragePrefix: config.StoragePrefix, + versionPrefixCache: &sync.Map{}, + } +} + // Policy is the struct used to store metadata type Policy struct { Name string `json:"name"` @@ -261,7 +318,7 @@ type Policy struct { // StoragePrefix is used to add a prefix when storing and retrieving the // policy object. - StoragePrefix string + StoragePrefix string `json:"storage_prefix"` // versionPrefixCache stores caches of verison prefix strings and the split // version template. From 2c5d298913d3f13ed54f52213a9440f3a7a4db4e Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 9 Mar 2018 10:43:15 -0500 Subject: [PATCH 20/45] Bump complete. Fixes #4094 --- .../posener/complete/cmd/install/utils.go | 9 +++++++++ vendor/vendor.json | 18 +++++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/vendor/github.com/posener/complete/cmd/install/utils.go b/vendor/github.com/posener/complete/cmd/install/utils.go index 8bcf4e1517d2..bb709bc6cd98 100644 --- a/vendor/github.com/posener/complete/cmd/install/utils.go +++ b/vendor/github.com/posener/complete/cmd/install/utils.go @@ -6,6 +6,7 @@ import ( "io" "io/ioutil" "os" + "path/filepath" ) func lineInFile(name string, lookFor string) bool { @@ -37,11 +38,19 @@ func lineInFile(name string, lookFor string) bool { } func createFile(name string, content string) error { + // make sure file directory exists + if err := os.MkdirAll(filepath.Dir(name), 0775); err != nil { + return err + } + + // create the file f, err := os.Create(name) if err != nil { return err } defer f.Close() + + // write file content _, err = f.WriteString(fmt.Sprintf("%s\n", content)) return err } diff --git a/vendor/vendor.json b/vendor/vendor.json index ea384f11ae04..3b4a9df53dd2 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1545,26 +1545,26 @@ { "checksumSHA1": "eKclqCehbe7JsvlemLF7TfjMWf0=", "path": "github.com/posener/complete", - "revision": "cdc49b71388c2ab059f57997ef2575c9e8b4f146", - "revisionTime": "2018-01-19T09:07:45Z" + "revision": "98eb9847f27ba2008d380a32c98be474dea55bdf", + "revisionTime": "2018-03-09T06:24:32Z" }, { "checksumSHA1": "NB7uVS0/BJDmNu68vPAlbrq4TME=", "path": "github.com/posener/complete/cmd", - "revision": "cdc49b71388c2ab059f57997ef2575c9e8b4f146", - "revisionTime": "2018-01-19T09:07:45Z" + "revision": "98eb9847f27ba2008d380a32c98be474dea55bdf", + "revisionTime": "2018-03-09T06:24:32Z" }, { - "checksumSHA1": "/HKxX422GpzWV56uW87cwXEWYV8=", + "checksumSHA1": "llSE1833yASSLHfDuN7lKx48020=", "path": "github.com/posener/complete/cmd/install", - "revision": "cdc49b71388c2ab059f57997ef2575c9e8b4f146", - "revisionTime": "2018-01-19T09:07:45Z" + "revision": "98eb9847f27ba2008d380a32c98be474dea55bdf", + "revisionTime": "2018-03-09T06:24:32Z" }, { "checksumSHA1": "DMo94FwJAm9ZCYCiYdJU2+bh4no=", "path": "github.com/posener/complete/match", - "revision": "cdc49b71388c2ab059f57997ef2575c9e8b4f146", - "revisionTime": "2018-01-19T09:07:45Z" + "revision": "98eb9847f27ba2008d380a32c98be474dea55bdf", + "revisionTime": "2018-03-09T06:24:32Z" }, { "checksumSHA1": "vCogt04lbcE8fUgvRCOaZQUo+Pk=", From d1c1a806270f8ede212256ba6309a5e9a936b792 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 9 Mar 2018 10:44:06 -0500 Subject: [PATCH 21/45] changelog++ --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d15ab499a9ca..25b019889001 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ DEPRECATIONS/CHANGES: - * The AWS authentication backend now allows binds for inputs, as either a + * The AWS authentication backend now allows binds for inputs as either a comma-delimited string or a string array. However, to keep consistency with input and output, when reading a role the binds will now be returned as string arrays rather than strings. @@ -23,6 +23,7 @@ BUG FIXES: set [GH-4107] * cli: Improve error messages around `vault auth help` when there is no CLI helper for a particular method [GH-4056] + * cli: Fix autocomplete installation when using Fish as the shell [GH-4094] * secret/ssh: Return `key_bits` value when reading a role [GH-4098] ## 0.9.5 (February 26th, 2018) From 1cd70cf0c9a00f376a9d8e332f2947d35edf376d Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 9 Mar 2018 10:53:04 -0500 Subject: [PATCH 22/45] Truncate token store issued token periods when greater than tuned max at (#4112) issue time, not just renew time. --- vault/expiration.go | 4 ++-- vault/token_store.go | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/vault/expiration.go b/vault/expiration.go index 2388c1c19d0d..2175784b2f9a 100644 --- a/vault/expiration.go +++ b/vault/expiration.go @@ -768,14 +768,14 @@ func (m *ExpirationManager) RenewToken(req *logical.Request, source string, toke // framework.LeaseExtend call against the request. Also, cap period value to // the sys/mount max value. if resp.Auth.Period > sysView.MaxLeaseTTL() { - retResp.AddWarning(fmt.Sprintf("Period of %s is greater than current mount/system default of %s, value will be truncated.", resp.Auth.TTL, sysView.MaxLeaseTTL())) + retResp.AddWarning(fmt.Sprintf("Period of %d seconds is greater than current mount/system default of %d seconds, value will be truncated.", int64(resp.Auth.TTL.Seconds()), int64(sysView.MaxLeaseTTL().Seconds()))) resp.Auth.Period = sysView.MaxLeaseTTL() } resp.Auth.TTL = resp.Auth.Period case resp.Auth.TTL > time.Duration(0): // Cap TTL value to the sys/mount max value if resp.Auth.TTL > sysView.MaxLeaseTTL() { - retResp.AddWarning(fmt.Sprintf("TTL of %s is greater than current mount/system default of %s, value will be truncated.", resp.Auth.TTL, sysView.MaxLeaseTTL())) + retResp.AddWarning(fmt.Sprintf("TTL of %d seconds is greater than current mount/system default of %d seconds, value will be truncated.", int64(resp.Auth.TTL.Seconds()), int64(sysView.MaxLeaseTTL().Seconds()))) resp.Auth.TTL = sysView.MaxLeaseTTL() } } diff --git a/vault/token_store.go b/vault/token_store.go index 51dbf3e645f8..0f6e00d28bf3 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -1893,6 +1893,12 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque sysView := ts.System() if periodToUse > 0 { + // Cap period value to the sys/mount max value; this matches behavior + // in expiration manager for renewals + if periodToUse > sysView.MaxLeaseTTL() { + resp.AddWarning(fmt.Sprintf("Period of %d seconds is greater than current mount/system default of %d seconds, value will be truncated.", int64(periodToUse.Seconds()), int64(sysView.MaxLeaseTTL().Seconds()))) + periodToUse = sysView.MaxLeaseTTL() + } te.TTL = periodToUse } else { // Set the default lease if not provided, root tokens are exempt From e566b0eed907181454f6d54e42e67356d41d7a01 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 9 Mar 2018 10:54:23 -0500 Subject: [PATCH 23/45] changelog++ --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25b019889001..302776e9d7f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,10 @@ BUG FIXES: * auth/aws: Fix honoring `max_ttl` when a corresponding role `ttl` is not also set [GH-4107] + * auth/token: If a periodic token being issued has a period greater than the + max_lease_ttl configured on the token store mount, truncate it. This matches + renewal behavior; before it was inconsistent between issuance and renewal. + [GH-4112] * cli: Improve error messages around `vault auth help` when there is no CLI helper for a particular method [GH-4056] * cli: Fix autocomplete installation when using Fish as the shell [GH-4094] From dcd032d38121104010248bf129aa5aafc5a82cf4 Mon Sep 17 00:00:00 2001 From: Alvin Huang Date: Fri, 9 Mar 2018 13:06:58 -0500 Subject: [PATCH 24/45] bump middleman-hashicorp container to 0.3.32 (#4117) --- website/packer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/packer.json b/website/packer.json index cbfb5c565ad3..aeaa162e0985 100644 --- a/website/packer.json +++ b/website/packer.json @@ -8,7 +8,7 @@ "builders": [ { "type": "docker", - "image": "hashicorp/middleman-hashicorp:0.3.29", + "image": "hashicorp/middleman-hashicorp:0.3.32", "discard": "true", "volumes": { "{{ pwd }}": "/website" From 0216f99727c1e7b74c203bf45e2963d8f4c75400 Mon Sep 17 00:00:00 2001 From: Brian Kassouf Date: Fri, 9 Mar 2018 11:01:24 -0800 Subject: [PATCH 25/45] helper/keysutil: Add a LoadPolicy function (#4116) * helper/keysutil: Add a LoadPolicy function * Use the load policy function in the lock manager --- helper/keysutil/lock_manager.go | 20 +------------------- helper/keysutil/policy.go | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/helper/keysutil/lock_manager.go b/helper/keysutil/lock_manager.go index 24b25f380c01..778c28cbcbb0 100644 --- a/helper/keysutil/lock_manager.go +++ b/helper/keysutil/lock_manager.go @@ -500,23 +500,5 @@ func (lm *LockManager) DeletePolicy(ctx context.Context, storage logical.Storage } func (lm *LockManager) getStoredPolicy(ctx context.Context, storage logical.Storage, name string) (*Policy, error) { - // Check if the policy already exists - raw, err := storage.Get(ctx, "policy/"+name) - if err != nil { - return nil, err - } - if raw == nil { - return nil, nil - } - - // Decode the policy - var policy Policy - err = jsonutil.DecodeJSON(raw.Value, &policy) - if err != nil { - return nil, err - } - - policy.versionPrefixCache = &sync.Map{} - - return &policy, nil + return LoadPolicy(ctx, storage, "policy/"+name) } diff --git a/helper/keysutil/policy.go b/helper/keysutil/policy.go index b13ebd5938b7..50bc3b868c30 100644 --- a/helper/keysutil/policy.go +++ b/helper/keysutil/policy.go @@ -262,6 +262,28 @@ func NewPolicy(config PolicyConfig) *Policy { } } +// LoadPolicy will load a policy from the provided storage path and set the +// necessary un-exported variables. It is particularly useful when accessing a +// policy without the lock manager. +func LoadPolicy(ctx context.Context, s logical.Storage, path string) (*Policy, error) { + raw, err := s.Get(ctx, path) + if err != nil { + return nil, err + } + if raw == nil { + return nil, nil + } + + var policy Policy + err = jsonutil.DecodeJSON(raw.Value, &policy) + if err != nil { + return nil, err + } + + policy.versionPrefixCache = &sync.Map{} + return &policy, nil +} + // Policy is the struct used to store metadata type Policy struct { Name string `json:"name"` From 034f83f1cdc77bc4fcb608b31b722b4032332e32 Mon Sep 17 00:00:00 2001 From: Calvin Leung Huang Date: Fri, 9 Mar 2018 14:32:28 -0500 Subject: [PATCH 26/45] Audit HMAC values on AuthConfig (#4077) * Add audit hmac values to AuthConfigInput and AuthConfigOutput, fix docs * docs: Add ttl params to auth enable endpoint * Rewording of go string to simply string * Add audit hmac keys as CLI flags on auth/secrets enable * Fix copypasta mistake * Add audit hmac keys to auth and secrets list * Only set config values if they exist * Fix http sys/auth tests * More auth plugin_name test fixes * Pass API values into MountEntry's config when creating auth/secrets mount * Update usage wording --- api/sys_auth.go | 16 ++++--- command/auth_enable.go | 48 +++++++++++++++---- command/base.go | 6 +++ command/commands.go | 5 ++ command/secrets_enable.go | 44 ++++++++++++++---- http/sys_auth_test.go | 8 ++++ vault/logical_system.go | 59 ++++++++++++++++++------ vault/logical_system_test.go | 3 ++ website/source/api/system/auth.html.md | 15 +++++- website/source/api/system/mounts.html.md | 22 +++++---- 10 files changed, 180 insertions(+), 46 deletions(-) diff --git a/api/sys_auth.go b/api/sys_auth.go index 937b0eafb4b9..fd9c5c59a3bb 100644 --- a/api/sys_auth.go +++ b/api/sys_auth.go @@ -91,9 +91,11 @@ type EnableAuthOptions struct { } type AuthConfigInput struct { - DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` - MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` - PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"` + DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` + MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` + PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"` + AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"` + AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"` } type AuthMount struct { @@ -106,7 +108,9 @@ type AuthMount struct { } type AuthConfigOutput struct { - DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` - MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` - PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"` + DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` + MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` + PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"` + AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"` + AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"` } diff --git a/command/auth_enable.go b/command/auth_enable.go index 328524eb81cb..c6b7486bd0a8 100644 --- a/command/auth_enable.go +++ b/command/auth_enable.go @@ -1,6 +1,7 @@ package command import ( + "flag" "fmt" "strings" "time" @@ -16,13 +17,15 @@ var _ cli.CommandAutocomplete = (*AuthEnableCommand)(nil) type AuthEnableCommand struct { *BaseCommand - flagDescription string - flagPath string - flagDefaultLeaseTTL time.Duration - flagMaxLeaseTTL time.Duration - flagPluginName string - flagLocal bool - flagSealWrap bool + flagDescription string + flagPath string + flagDefaultLeaseTTL time.Duration + flagMaxLeaseTTL time.Duration + flagAuditNonHMACRequestKeys []string + flagAuditNonHMACResponseKeys []string + flagPluginName string + flagLocal bool + flagSealWrap bool } func (c *AuthEnableCommand) Synopsis() string { @@ -96,6 +99,20 @@ func (c *AuthEnableCommand) Flags() *FlagSets { "TTL.", }) + f.StringSliceVar(&StringSliceVar{ + Name: flagNameAuditNonHMACRequestKeys, + Target: &c.flagAuditNonHMACRequestKeys, + Usage: "Comma-separated string or list of keys that will not be HMAC'd by audit" + + "devices in the request data object.", + }) + + f.StringSliceVar(&StringSliceVar{ + Name: flagNameAuditNonHMACResponseKeys, + Target: &c.flagAuditNonHMACResponseKeys, + Usage: "Comma-separated string or list of keys that will not be HMAC'd by audit" + + "devices in the response data object.", + }) + f.StringVar(&StringVar{ Name: "plugin-name", Target: &c.flagPluginName, @@ -170,7 +187,7 @@ func (c *AuthEnableCommand) Run(args []string) int { // Append a trailing slash to indicate it's a path in output authPath = ensureTrailingSlash(authPath) - if err := client.Sys().EnableAuthWithOptions(authPath, &api.EnableAuthOptions{ + authOpts := &api.EnableAuthOptions{ Type: authType, Description: c.flagDescription, Local: c.flagLocal, @@ -180,7 +197,20 @@ func (c *AuthEnableCommand) Run(args []string) int { MaxLeaseTTL: c.flagMaxLeaseTTL.String(), PluginName: c.flagPluginName, }, - }); err != nil { + } + + // Set these values only if they are provided in the CLI + f.Visit(func(fl *flag.Flag) { + if fl.Name == flagNameAuditNonHMACRequestKeys { + authOpts.Config.AuditNonHMACRequestKeys = c.flagAuditNonHMACRequestKeys + } + + if fl.Name == flagNameAuditNonHMACResponseKeys { + authOpts.Config.AuditNonHMACRequestKeys = c.flagAuditNonHMACResponseKeys + } + }) + + if err := client.Sys().EnableAuthWithOptions(authPath, authOpts); err != nil { c.UI.Error(fmt.Sprintf("Error enabling %s auth: %s", authType, err)) return 2 } diff --git a/command/base.go b/command/base.go index 26c62dd64fdb..21c7518b760e 100644 --- a/command/base.go +++ b/command/base.go @@ -332,6 +332,12 @@ func (f *FlagSets) Args() []string { return f.mainSet.Args() } +// Visit visits the flags in lexicographical order, calling fn for each. It +// visits only those flags that have been set. +func (f *FlagSets) Visit(fn func(*flag.Flag)) { + f.mainSet.Visit(fn) +} + // Help builds custom help for this command, grouping by flag set. func (fs *FlagSets) Help() string { var out bytes.Buffer diff --git a/command/commands.go b/command/commands.go index 71d7d5bfd0b0..366d162809cb 100644 --- a/command/commands.go +++ b/command/commands.go @@ -71,6 +71,11 @@ const ( EnvVaultCLINoColor = `VAULT_CLI_NO_COLOR` // EnvVaultFormat is the output format EnvVaultFormat = `VAULT_FORMAT` + + // flagNameAuditNonHMACRequestKeys is the flag name used for auth/secrets enable + flagNameAuditNonHMACRequestKeys = "audit-non-hmac-request-keys" + // flagNameAuditNonHMACResponseKeys is the flag name used for auth/secrets enable + flagNameAuditNonHMACResponseKeys = "audit-non-hmac-response-keys" ) var ( diff --git a/command/secrets_enable.go b/command/secrets_enable.go index e31d77ec2456..ad464a5ef518 100644 --- a/command/secrets_enable.go +++ b/command/secrets_enable.go @@ -1,6 +1,7 @@ package command import ( + "flag" "fmt" "strings" "time" @@ -16,14 +17,16 @@ var _ cli.CommandAutocomplete = (*SecretsEnableCommand)(nil) type SecretsEnableCommand struct { *BaseCommand - flagDescription string - flagPath string - flagDefaultLeaseTTL time.Duration - flagMaxLeaseTTL time.Duration - flagForceNoCache bool - flagPluginName string - flagLocal bool - flagSealWrap bool + flagDescription string + flagPath string + flagDefaultLeaseTTL time.Duration + flagMaxLeaseTTL time.Duration + flagAuditNonHMACRequestKeys []string + flagAuditNonHMACResponseKeys []string + flagForceNoCache bool + flagPluginName string + flagLocal bool + flagSealWrap bool } func (c *SecretsEnableCommand) Synopsis() string { @@ -104,6 +107,20 @@ func (c *SecretsEnableCommand) Flags() *FlagSets { "TTL.", }) + f.StringSliceVar(&StringSliceVar{ + Name: flagNameAuditNonHMACRequestKeys, + Target: &c.flagAuditNonHMACRequestKeys, + Usage: "Comma-separated string or list of keys that will not be HMAC'd by audit" + + "devices in the request data object.", + }) + + f.StringSliceVar(&StringSliceVar{ + Name: flagNameAuditNonHMACResponseKeys, + Target: &c.flagAuditNonHMACResponseKeys, + Usage: "Comma-separated string or list of keys that will not be HMAC'd by audit" + + "devices in the response data object.", + }) + f.BoolVar(&BoolVar{ Name: "force-no-cache", Target: &c.flagForceNoCache, @@ -202,6 +219,17 @@ func (c *SecretsEnableCommand) Run(args []string) int { }, } + // Set these values only if they are provided in the CLI + f.Visit(func(fl *flag.Flag) { + if fl.Name == flagNameAuditNonHMACRequestKeys { + mountInput.Config.AuditNonHMACRequestKeys = c.flagAuditNonHMACRequestKeys + } + + if fl.Name == flagNameAuditNonHMACResponseKeys { + mountInput.Config.AuditNonHMACRequestKeys = c.flagAuditNonHMACResponseKeys + } + }) + if err := client.Sys().Mount(mountPath, mountInput); err != nil { c.UI.Error(fmt.Sprintf("Error enabling: %s", err)) return 2 diff --git a/http/sys_auth_test.go b/http/sys_auth_test.go index 58e70963a772..a806450f36a4 100644 --- a/http/sys_auth_test.go +++ b/http/sys_auth_test.go @@ -31,6 +31,7 @@ func TestSysAuth(t *testing.T) { "config": map[string]interface{}{ "default_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"), + "plugin_name": "", }, "local": false, "seal_wrap": false, @@ -42,6 +43,7 @@ func TestSysAuth(t *testing.T) { "config": map[string]interface{}{ "default_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"), + "plugin_name": "", }, "local": false, "seal_wrap": false, @@ -93,6 +95,7 @@ func TestSysEnableAuth(t *testing.T) { "config": map[string]interface{}{ "default_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"), + "plugin_name": "", }, "local": false, "seal_wrap": false, @@ -103,6 +106,7 @@ func TestSysEnableAuth(t *testing.T) { "config": map[string]interface{}{ "default_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"), + "plugin_name": "", }, "local": false, "seal_wrap": false, @@ -114,6 +118,7 @@ func TestSysEnableAuth(t *testing.T) { "config": map[string]interface{}{ "default_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"), + "plugin_name": "", }, "local": false, "seal_wrap": false, @@ -124,6 +129,7 @@ func TestSysEnableAuth(t *testing.T) { "config": map[string]interface{}{ "default_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"), + "plugin_name": "", }, "local": false, "seal_wrap": false, @@ -176,6 +182,7 @@ func TestSysDisableAuth(t *testing.T) { "config": map[string]interface{}{ "default_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"), + "plugin_name": "", }, "description": "token based credentials", "type": "token", @@ -187,6 +194,7 @@ func TestSysDisableAuth(t *testing.T) { "config": map[string]interface{}{ "default_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"), + "plugin_name": "", }, "description": "token based credentials", "type": "token", diff --git a/vault/logical_system.go b/vault/logical_system.go index f2915065f20e..c632c8805bc5 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -1443,15 +1443,22 @@ func (b *SystemBackend) handleMountTable(ctx context.Context, req *logical.Reque "type": entry.Type, "description": entry.Description, "accessor": entry.Accessor, - "config": map[string]interface{}{ - "default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()), - "max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()), - "force_no_cache": entry.Config.ForceNoCache, - "plugin_name": entry.Config.PluginName, - }, - "local": entry.Local, - "seal_wrap": entry.SealWrap, + "local": entry.Local, + "seal_wrap": entry.SealWrap, + } + entryConfig := map[string]interface{}{ + "default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()), + "max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()), + "force_no_cache": entry.Config.ForceNoCache, + "plugin_name": entry.Config.PluginName, + } + if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok { + entryConfig["audit_non_hmac_request_keys"] = rawVal.([]string) + } + if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_response_keys"); ok { + entryConfig["audit_non_hmac_response_keys"] = rawVal.([]string) } + info["config"] = entryConfig resp.Data[entry.Path] = info } @@ -1553,6 +1560,14 @@ func (b *SystemBackend) handleMount(ctx context.Context, req *logical.Request, d config.ForceNoCache = true } + if len(apiConfig.AuditNonHMACRequestKeys) > 0 { + config.AuditNonHMACRequestKeys = apiConfig.AuditNonHMACRequestKeys + } + + if len(apiConfig.AuditNonHMACResponseKeys) > 0 { + config.AuditNonHMACResponseKeys = apiConfig.AuditNonHMACResponseKeys + } + // Create the mount entry me := &MountEntry{ Table: mountTableType, @@ -2028,13 +2043,21 @@ func (b *SystemBackend) handleAuthTable(ctx context.Context, req *logical.Reques "type": entry.Type, "description": entry.Description, "accessor": entry.Accessor, - "config": map[string]interface{}{ - "default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()), - "max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()), - }, - "local": entry.Local, - "seal_wrap": entry.SealWrap, + "local": entry.Local, + "seal_wrap": entry.SealWrap, + } + entryConfig := map[string]interface{}{ + "default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()), + "max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()), + "plugin_name": entry.Config.PluginName, + } + if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok { + entryConfig["audit_non_hmac_request_keys"] = rawVal.([]string) + } + if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_response_keys"); ok { + entryConfig["audit_non_hmac_response_keys"] = rawVal.([]string) } + info["config"] = entryConfig resp.Data[entry.Path] = info } return resp, nil @@ -2129,6 +2152,14 @@ func (b *SystemBackend) handleEnableAuth(ctx context.Context, req *logical.Reque path = sanitizeMountPath(path) + if len(apiConfig.AuditNonHMACRequestKeys) > 0 { + config.AuditNonHMACRequestKeys = apiConfig.AuditNonHMACRequestKeys + } + + if len(apiConfig.AuditNonHMACResponseKeys) > 0 { + config.AuditNonHMACResponseKeys = apiConfig.AuditNonHMACResponseKeys + } + // Create the mount entry me := &MountEntry{ Table: credentialTableType, diff --git a/vault/logical_system_test.go b/vault/logical_system_test.go index a5bfd37043cd..5ee285bfe4d5 100644 --- a/vault/logical_system_test.go +++ b/vault/logical_system_test.go @@ -1388,6 +1388,7 @@ func TestSystemBackend_authTable(t *testing.T) { "config": map[string]interface{}{ "default_lease_ttl": int64(0), "max_lease_ttl": int64(0), + "plugin_name": "", }, "local": false, "seal_wrap": false, @@ -1438,6 +1439,7 @@ func TestSystemBackend_enableAuth(t *testing.T) { "config": map[string]interface{}{ "default_lease_ttl": int64(2100), "max_lease_ttl": int64(2700), + "plugin_name": "", }, "local": true, "seal_wrap": true, @@ -1449,6 +1451,7 @@ func TestSystemBackend_enableAuth(t *testing.T) { "config": map[string]interface{}{ "default_lease_ttl": int64(0), "max_lease_ttl": int64(0), + "plugin_name": "", }, "local": false, "seal_wrap": false, diff --git a/website/source/api/system/auth.html.md b/website/source/api/system/auth.html.md index 4b72e9aa4cf2..2f85c191a780 100644 --- a/website/source/api/system/auth.html.md +++ b/website/source/api/system/auth.html.md @@ -77,7 +77,20 @@ For example, enable the "foo" auth method will make it accessible at - `config` `(map: nil)` – Specifies configuration options for this auth method. These are the possible values: - - `plugin_name` + - `default_lease_ttl` `(string: "")` - The default lease duration, specified + as a string duration like "5s" or "30m". + + - `max_lease_ttl` `(string: "")` - The maximum lease duration, specified as a + string duration like "5s" or "30m". + + - `plugin_name` `(string: "")` - The name of the plugin in the plugin catalog + to use. + + - `audit_non_hmac_request_keys` `(array: [])` - Comma-separated list of keys + that will not be HMAC'd by audit devices in the request data object. + + - `audit_non_hmac_response_keys` `(array: [])` - Comma-separated list of keys + that will not be HMAC'd by audit devices in the response data object. The plugin_name can be provided in the config map or as a top-level option, with the former taking precedence. diff --git a/website/source/api/system/mounts.html.md b/website/source/api/system/mounts.html.md index bd52bdea9be0..d2a69900c062 100644 --- a/website/source/api/system/mounts.html.md +++ b/website/source/api/system/mounts.html.md @@ -80,23 +80,29 @@ This endpoint enables a new secrets engine at the given path. - `config` `(map: nil)` – Specifies configuration options for this mount. This is an object with four possible values: - - `default_lease_ttl` `(string: "")` - the default lease duration, specified - as a go string duration like "5s" or "30m". + - `default_lease_ttl` `(string: "")` - The default lease duration, specified + as a string duration like "5s" or "30m". - - `max_lease_ttl` `(string: "")` - the maximum lease duration, specified as - a go string duration like "5s" or "30m". + - `max_lease_ttl` `(string: "")` - The maximum lease duration, specified as a + string duration like "5s" or "30m". - - `force_no_cache` `(bool: false)` - disable caching. + - `force_no_cache` `(bool: false)` - Disable caching. - - `plugin_name` `(string: "")` - the name of the plugin in the plugin - catalog to use. + - `plugin_name` `(string: "")` - The name of the plugin in the plugin catalog + to use. + + - `audit_non_hmac_request_keys` `(array: [])` - Comma-separated list of keys + that will not be HMAC'd by audit devices in the request data object. + + - `audit_non_hmac_response_keys` `(array: [])` - Comma-separated list of keys + that will not be HMAC'd by audit devices in the response data object. These control the default and maximum lease time-to-live, force disabling backend caching, and option plugin name for plugin backends respectively. The first three options override the global defaults if set on a specific mount. The plugin_name can be provided in the config map or as a top-level option, with the former taking precedence. - + When used with supported seals (`pkcs11`, `awskms`, etc.), `seal_wrap` causes key material for supporting mounts to be wrapped by the seal's encryption capability. This is currently only supported for `transit` and From dc77e3010673e049b3dbdc167b3d852921e1c072 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Sat, 10 Mar 2018 03:37:29 -0500 Subject: [PATCH 27/45] changelog++ --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 302776e9d7f3..758b533a6ed5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ DEPRECATIONS/CHANGES: IMPROVEMENTS: + * auth/approle: Allow array input for bound_cidr_list [4078] * auth/aws: Allow using lists in role bind parameters [GH-3907] * server: Make sure `tls_disable_client_cert` is actually a true value rather than just set [GH-4049] From c248375372a039ad7cb4d5475cd98c15ad206c09 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 13 Mar 2018 15:28:09 +0100 Subject: [PATCH 28/45] [doc] Change auth token in getting-started (#4118) In the authentication section of the getting started doc, the token used to login doesn't match with the one displayed as the command result. This commit makes sure that both tokens correspond to avoid distracting newcomers. --- website/source/intro/getting-started/authentication.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/intro/getting-started/authentication.html.md b/website/source/intro/getting-started/authentication.html.md index 55f90ab57402..d8a2632f63f4 100644 --- a/website/source/intro/getting-started/authentication.html.md +++ b/website/source/intro/getting-started/authentication.html.md @@ -86,7 +86,7 @@ is only used for revoking _leases_. For revoking _tokens_, use To authenticate with a token: ```text -$ vault login d08e2bd5-ffb0-440d-6486-b8f650ec8c0c +$ vault login a402d075-6d59-6129-1ac7-3718796d4346 Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run "vault login" again. Future Vault requests will automatically use this token. From f8324e9c2a806c6821f9ae8472da6df8f8a39841 Mon Sep 17 00:00:00 2001 From: Brian Shumate Date: Tue, 13 Mar 2018 10:32:28 -0400 Subject: [PATCH 29/45] Docs: grammatical clarification around community supported note (#4122) --- website/source/docs/configuration/storage/dynamodb.html.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/docs/configuration/storage/dynamodb.html.md b/website/source/docs/configuration/storage/dynamodb.html.md index 61ca9e687e87..0f68749e7234 100644 --- a/website/source/docs/configuration/storage/dynamodb.html.md +++ b/website/source/docs/configuration/storage/dynamodb.html.md @@ -19,8 +19,8 @@ The DynamoDB storage backend is used to persist Vault's data in - **Community Supported** – the DynamoDB storage backend is supported by the community. While it has undergone review by HashiCorp employees, they may not - be as knowledgeable about the technology. If you encounter problems with them, - you may be referred to the original author. + be as knowledgeable about the technology. If you encounter problems with this + storage backend, you could be referred to the original author for support. ```hcl storage "dynamodb" { From 025825dfe066608f94683070870b20371342c22b Mon Sep 17 00:00:00 2001 From: Joel Thompson Date: Tue, 13 Mar 2018 10:35:10 -0400 Subject: [PATCH 30/45] Accept temp creds in AWS secret backend acceptance tests (#4076) * Accept temp creds in AWS secret backend acceptance tests The AWS secret backend acceptance tests implicitly accepted long-lived AWS credentials (i.e., AWS IAM user and/or root credentials) in two ways: 1. It expected credentials to be passed in via the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables. By not accepting AWS_SESSION_TOKEN or AWS_SECURITY_TOKEN, temporary credentials could not be passed in. (This also forced all credentials to be passed in via environment variables, which is a bit ugly). 2. The AWS sts:GetFederationToken call is only allowed from long-term credentials. This is called by the Vault code which the acceptance tests exercise. 1 is solved by deleting explicit references to credentials, which allows the SDK to do one of the things it does best -- find credentials via the default chain. 2 is a little more complicated. Rather than pass in whatever creds the acceptance test was run under to the backend, the acceptance test now creates a new IAM user and gets an access key from it, then passes the IAM user's creds back to the backend so that it can call sts:GetFederationToken (and then tries to clean up afterwards). * Fix Travis build failure The Travis build was failing because the user creation was happening regardless of whether it was running in acceptance test mode or not. This moves the user creation into the acceptance test precheck, which requires lazily evaluating the credentials when configuring the backend in the STS accetpance test, and so moving that to a PreFlight closure. * Reduce blind sleeps in AWS secret backend acceptance tests This removes a blind "sleep 10 seconds and then attempt to reuse the credential" codepath and instead just keeps attemtping to reuse the credential for 10 seconds and fails if there aren't any successful uses after 10 seconds. This adds a few seconds speedup of acceptance test runs from my experiments. --- builtin/logical/aws/backend_test.go | 232 ++++++++++++++++++++++------ 1 file changed, 182 insertions(+), 50 deletions(-) diff --git a/builtin/logical/aws/backend_test.go b/builtin/logical/aws/backend_test.go index ad919b1bee23..1973a2ef8197 100644 --- a/builtin/logical/aws/backend_test.go +++ b/builtin/logical/aws/backend_test.go @@ -7,7 +7,6 @@ import ( "fmt" "log" "os" - "strings" "testing" "time" @@ -16,6 +15,7 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/iam" + "github.com/aws/aws-sdk-go/service/sts" "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/vault/logical" logicaltest "github.com/hashicorp/vault/logical/testing" @@ -41,15 +41,21 @@ func TestBackend_basic(t *testing.T) { } func TestBackend_basicSTS(t *testing.T) { + accessKey := &awsAccessKey{} logicaltest.Test(t, logicaltest.TestCase{ AcceptanceTest: true, PreCheck: func() { testAccPreCheck(t) + createUser(t, accessKey) createRole(t) + // Sleep sometime because AWS is eventually consistent + // Both the createUser and createRole depend on this + log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...") + time.Sleep(10 * time.Second) }, Backend: getBackend(t), Steps: []logicaltest.TestStep{ - testAccStepConfig(t), + testAccStepConfigWithCreds(t, accessKey), testAccStepWritePolicy(t, "test", testPolicy), testAccStepReadSTS(t, "test"), testAccStepWriteArnPolicyRef(t, "test", testPolicyArn), @@ -57,7 +63,9 @@ func TestBackend_basicSTS(t *testing.T) { testAccStepWriteArnRoleRef(t, testRoleName), testAccStepReadSTS(t, testRoleName), }, - Teardown: teardown, + Teardown: func() error { + return teardown(accessKey) + }, }) } @@ -81,14 +89,6 @@ func TestBackend_policyCrud(t *testing.T) { } func testAccPreCheck(t *testing.T) { - if v := os.Getenv("AWS_ACCESS_KEY_ID"); v == "" { - t.Fatal("AWS_ACCESS_KEY_ID must be set for acceptance tests") - } - - if v := os.Getenv("AWS_SECRET_ACCESS_KEY"); v == "" { - t.Fatal("AWS_SECRET_ACCESS_KEY must be set for acceptance tests") - } - if v := os.Getenv("AWS_DEFAULT_REGION"); v == "" { log.Println("[INFO] Test: Using us-west-2 as test region") os.Setenv("AWS_DEFAULT_REGION", "us-west-2") @@ -97,7 +97,7 @@ func testAccPreCheck(t *testing.T) { if v := os.Getenv("AWS_ACCOUNT_ID"); v == "" { accountId, err := getAccountId() if err != nil { - t.Fatal("AWS_ACCOUNT_ID could not be read from iam:GetUser for acceptance tests") + t.Fatalf("AWS_ACCOUNT_ID could not be read from iam:GetUser for acceptance tests: %#v", err) } log.Printf("[INFO] Test: Used %s as AWS_ACCOUNT_ID", accountId) os.Setenv("AWS_ACCOUNT_ID", accountId) @@ -105,27 +105,23 @@ func testAccPreCheck(t *testing.T) { } func getAccountId() (string, error) { - creds := credentials.NewStaticCredentials(os.Getenv("AWS_ACCESS_KEY_ID"), - os.Getenv("AWS_SECRET_ACCESS_KEY"), - "") - awsConfig := &aws.Config{ - Credentials: creds, - Region: aws.String("us-east-1"), - HTTPClient: cleanhttp.DefaultClient(), + Region: aws.String("us-east-1"), + HTTPClient: cleanhttp.DefaultClient(), } - svc := iam.New(session.New(awsConfig)) + svc := sts.New(session.New(awsConfig)) - params := &iam.GetUserInput{} - res, err := svc.GetUser(params) + params := &sts.GetCallerIdentityInput{} + res, err := svc.GetCallerIdentity(params) if err != nil { return "", err } + if res == nil { + return "", fmt.Errorf("got nil response from GetCallerIdentity") + } - // split "arn:aws:iam::012345678912:user/username" - accountId := strings.Split(*res.User.Arn, ":")[4] - return accountId, nil + return *res.Account, nil } const testRoleName = "Vault-Acceptance-Test-AWS-Assume-Role" @@ -144,12 +140,9 @@ func createRole(t *testing.T) { ] } ` - creds := credentials.NewStaticCredentials(os.Getenv("AWS_ACCESS_KEY_ID"), os.Getenv("AWS_SECRET_ACCESS_KEY"), "") - awsConfig := &aws.Config{ - Credentials: creds, - Region: aws.String("us-east-1"), - HTTPClient: cleanhttp.DefaultClient(), + Region: aws.String("us-east-1"), + HTTPClient: cleanhttp.DefaultClient(), } svc := iam.New(session.New(awsConfig)) trustPolicy := fmt.Sprintf(testRoleAssumePolicy, os.Getenv("AWS_ACCOUNT_ID")) @@ -176,19 +169,94 @@ func createRole(t *testing.T) { if err != nil { t.Fatalf("AWS CreateRole failed: %v", err) } - - // Sleep sometime because AWS is eventually consistent - log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...") - time.Sleep(10 * time.Second) } -func teardown() error { - creds := credentials.NewStaticCredentials(os.Getenv("AWS_ACCESS_KEY_ID"), os.Getenv("AWS_SECRET_ACCESS_KEY"), "") +const testUserName = "Vault-Acceptance-Test-AWS-FederationToken" + +func createUser(t *testing.T, accessKey *awsAccessKey) { + // The sequence of user creation actions is carefully chosen to minimize + // impact of stolen IAM user credentials + // 1. Create user, without any permissions or credentials. At this point, + // nobody cares if creds compromised because this user can do nothing. + // 2. Attach the timebomb policy. This grants no access but puts a time limit + // on validitity of compromised credentials. If this fails, nobody cares + // because the user has no permissions to do anything anyway + // 3. Attach the AdminAccess policy. The IAM user still has no credentials to + // do anything + // 4. Generate API creds to get an actual access key and secret key + timebombPolicyTemplate := `{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Deny", + "Action": "*", + "Resource": "*", + "Condition": { + "DateGreaterThan": { + "aws:CurrentTime": "%s" + } + } + } + ] + } + ` + validity := time.Duration(2 * time.Hour) + expiry := time.Now().Add(validity) + timebombPolicy := fmt.Sprintf(timebombPolicyTemplate, expiry.Format(time.RFC3339)) + awsConfig := &aws.Config{ + Region: aws.String("us-east-1"), + HTTPClient: cleanhttp.DefaultClient(), + } + svc := iam.New(session.New(awsConfig)) + + createUserInput := &iam.CreateUserInput{ + UserName: aws.String(testUserName), + } + log.Printf("[INFO] AWS CreateUser: %s", testUserName) + _, err := svc.CreateUser(createUserInput) + if err != nil { + t.Fatalf("AWS CreateUser failed: %v", err) + } + + putPolicyInput := &iam.PutUserPolicyInput{ + PolicyDocument: aws.String(timebombPolicy), + PolicyName: aws.String("SelfDestructionTimebomb"), + UserName: aws.String(testUserName), + } + _, err = svc.PutUserPolicy(putPolicyInput) + if err != nil { + t.Fatalf("AWS PutUserPolicy failed: %v", err) + } + + attachUserPolicyInput := &iam.AttachUserPolicyInput{ + PolicyArn: aws.String("arn:aws:iam::aws:policy/AdministratorAccess"), + UserName: aws.String(testUserName), + } + _, err = svc.AttachUserPolicy(attachUserPolicyInput) + if err != nil { + t.Fatalf("AWS AttachUserPolicy failed, %v", err) + } + + createAccessKeyInput := &iam.CreateAccessKeyInput{ + UserName: aws.String(testUserName), + } + createAccessKeyOutput, err := svc.CreateAccessKey(createAccessKeyInput) + if err != nil { + t.Fatalf("AWS CreateAccessKey failed: %v", err) + } + if createAccessKeyOutput == nil { + t.Fatalf("AWS CreateAccessKey returned nil") + } + genAccessKey := createAccessKeyOutput.AccessKey + + accessKey.AccessKeyId = *genAccessKey.AccessKeyId + accessKey.SecretAccessKey = *genAccessKey.SecretAccessKey +} +func teardown(accessKey *awsAccessKey) error { awsConfig := &aws.Config{ - Credentials: creds, - Region: aws.String("us-east-1"), - HTTPClient: cleanhttp.DefaultClient(), + Region: aws.String("us-east-1"), + HTTPClient: cleanhttp.DefaultClient(), } svc := iam.New(session.New(awsConfig)) @@ -214,6 +282,45 @@ func teardown() error { return err } + userDetachment := &iam.DetachUserPolicyInput{ + PolicyArn: aws.String("arn:aws:iam::aws:policy/AdministratorAccess"), + UserName: aws.String(testUserName), + } + _, err = svc.DetachUserPolicy(userDetachment) + if err != nil { + log.Printf("[WARN] AWS DetachUserPolicy failed: %v", err) + return err + } + + deleteAccessKeyInput := &iam.DeleteAccessKeyInput{ + AccessKeyId: aws.String(accessKey.AccessKeyId), + UserName: aws.String(testUserName), + } + _, err = svc.DeleteAccessKey(deleteAccessKeyInput) + if err != nil { + log.Printf("[WARN] AWS DeleteAccessKey failed: %v", err) + return err + } + + deleteUserPolicyInput := &iam.DeleteUserPolicyInput{ + PolicyName: aws.String("SelfDestructionTimebomb"), + UserName: aws.String(testUserName), + } + _, err = svc.DeleteUserPolicy(deleteUserPolicyInput) + if err != nil { + log.Printf("[WARN] AWS DeleteUserPolicy failed: %v", err) + return err + } + deleteUserInput := &iam.DeleteUserInput{ + UserName: aws.String(testUserName), + } + log.Printf("[INFO] AWS DeleteUser: %s", testUserName) + _, err = svc.DeleteUser(deleteUserInput) + if err != nil { + log.Printf("[WARN] AWS DeleteUser failed: %v", err) + return err + } + return nil } @@ -222,9 +329,26 @@ func testAccStepConfig(t *testing.T) logicaltest.TestStep { Operation: logical.UpdateOperation, Path: "config/root", Data: map[string]interface{}{ - "access_key": os.Getenv("AWS_ACCESS_KEY_ID"), - "secret_key": os.Getenv("AWS_SECRET_ACCESS_KEY"), - "region": os.Getenv("AWS_DEFAULT_REGION"), + "region": os.Getenv("AWS_DEFAULT_REGION"), + }, + } +} + +func testAccStepConfigWithCreds(t *testing.T, accessKey *awsAccessKey) logicaltest.TestStep { + return logicaltest.TestStep{ + Operation: logical.UpdateOperation, + Path: "config/root", + Data: map[string]interface{}{ + "region": os.Getenv("AWS_DEFAULT_REGION"), + }, + PreFlight: func(req *logical.Request) error { + // Values in Data above get eagerly evaluated due to the testing framework. + // In particular, they get evaluated before accessKey gets set by CreateUser + // and thus would fail. By moving to a closure in a PreFlight, we ensure that + // the creds get evaluated lazily after they've been properly set + req.Data["access_key"] = accessKey.AccessKeyId + req.Data["secret_key"] = accessKey.SecretAccessKey + return nil }, } } @@ -243,10 +367,6 @@ func testAccStepReadUser(t *testing.T, name string) logicaltest.TestStep { } log.Printf("[WARN] Generated credentials: %v", d) - // Sleep sometime because AWS is eventually consistent - log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...") - time.Sleep(10 * time.Second) - // Build a client and verify that the credentials work creds := credentials.NewStaticCredentials(d.AccessKey, d.SecretKey, "") awsConfig := &aws.Config{ @@ -257,12 +377,19 @@ func testAccStepReadUser(t *testing.T, name string) logicaltest.TestStep { client := ec2.New(session.New(awsConfig)) log.Printf("[WARN] Verifying that the generated credentials work...") - _, err := client.DescribeInstances(&ec2.DescribeInstancesInput{}) - if err != nil { - return err + retryCount := 0 + success := false + var err error + for !success && retryCount < 10 { + _, err = client.DescribeInstances(&ec2.DescribeInstancesInput{}) + if err == nil { + return nil + } + time.Sleep(time.Second) + retryCount++ } - return nil + return err }, } } @@ -458,3 +585,8 @@ func testAccStepWriteArnRoleRef(t *testing.T, roleName string) logicaltest.TestS }, } } + +type awsAccessKey struct { + AccessKeyId string + SecretAccessKey string +} From b3c9358b0452ce0486e93454b44d73fb41edfd3e Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 13 Mar 2018 10:39:51 -0400 Subject: [PATCH 31/45] Have Okta respect its set max_ttl. (#4111) Fixes #4110 --- builtin/credential/okta/path_login.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/builtin/credential/okta/path_login.go b/builtin/credential/okta/path_login.go index 331af5a776a7..a7a719e2423d 100644 --- a/builtin/credential/okta/path_login.go +++ b/builtin/credential/okta/path_login.go @@ -96,6 +96,21 @@ func (b *backend) pathLogin(ctx context.Context, req *logical.Request, d *framew }, } + if resp.Auth.TTL == 0 { + resp.Auth.TTL = b.System().DefaultLeaseTTL() + } + if cfg.MaxTTL > 0 { + maxTTL := cfg.MaxTTL + if maxTTL > b.System().MaxLeaseTTL() { + maxTTL = b.System().MaxLeaseTTL() + } + + if resp.Auth.TTL > maxTTL { + resp.Auth.TTL = maxTTL + resp.AddWarning(fmt.Sprintf("Effective TTL of '%s' exceeded the effective max_ttl of '%s'; TTL value is capped accordingly", resp.Auth.TTL, maxTTL)) + } + } + for _, groupName := range groupNames { if groupName == "" { continue From 0de452dc2544c8fd59ae3e9830a4f0e1f0648a5f Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 13 Mar 2018 10:40:50 -0400 Subject: [PATCH 32/45] changelog++ --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 758b533a6ed5..02c63cdeb5cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ BUG FIXES: * auth/aws: Fix honoring `max_ttl` when a corresponding role `ttl` is not also set [GH-4107] + * auth/okta: Fix honoring configured `max_ttl` value [GH-4110] * auth/token: If a periodic token being issued has a period greater than the max_lease_ttl configured on the token store mount, truncate it. This matches renewal behavior; before it was inconsistent between issuance and renewal. From b3ccf7aac96e6a873df2b14b6edf5a3165076049 Mon Sep 17 00:00:00 2001 From: Joel Thompson Date: Wed, 14 Mar 2018 01:44:41 -0400 Subject: [PATCH 33/45] docs: Alphabetize CLI commands (#4127) status was appearing after token when it should be before --- website/source/layouts/docs.erb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index 1a299eb7b3ed..9c383b6ff4eb 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -308,6 +308,9 @@ > ssh + > + status + > token

- > - status - > unwrap From 26d8b7f095700a9ff7b81a3be1caceb607e506a4 Mon Sep 17 00:00:00 2001 From: Malte Date: Wed, 14 Mar 2018 08:45:21 +0100 Subject: [PATCH 34/45] Fix typo in recommended vault auth iam policy (#4128) The resource arn for the `sts:AssumeRole` action is missing a `:` for the region and therefore invalid. --- website/source/docs/auth/aws.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/auth/aws.html.md b/website/source/docs/auth/aws.html.md index a7cca908260b..6152d1a846ce 100644 --- a/website/source/docs/auth/aws.html.md +++ b/website/source/docs/auth/aws.html.md @@ -304,7 +304,7 @@ method. "Effect": "Allow", "Action": ["sts:AssumeRole"], "Resource": [ - "arn:aws:iam::role/" + "arn:aws:iam:::role/" ] } ] From 1adda1529980b42e3ec352650576b31d909d1b67 Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" Date: Wed, 14 Mar 2018 10:24:29 -0400 Subject: [PATCH 35/45] Vault user needed to use STS Federation Tokens (#4108) If you try to use role authorization to get an STS token, you'll get this error: * Error generating STS keys: AccessDenied: Cannot call GetFederationToken with session credentials --- website/source/docs/secrets/aws/index.html.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/source/docs/secrets/aws/index.html.md b/website/source/docs/secrets/aws/index.html.md index db07f2a766a1..fb2948ef73da 100644 --- a/website/source/docs/secrets/aws/index.html.md +++ b/website/source/docs/secrets/aws/index.html.md @@ -46,7 +46,9 @@ the IAM credentials: on IAM credentials. Since Vault uses the official AWS SDK, it will use the specified credentials. You can also specify the credentials via the standard AWS environment credentials, shared file credentials, or IAM role/ECS task - credentials. + credentials. (Note that you can't authorize vault with IAM role credentials if you plan + on using STS Federation Tokens, since the temporary security credentials + associated with the role are not authorized to use GetFederationToken.) ~> **Notice:** Even though the path above is `aws/config/root`, do not use your AWS root account credentials. Instead generate a dedicated user or From 17ed6663f7bee836c94d9cee4cd999863b30f14d Mon Sep 17 00:00:00 2001 From: Jim Kalafut Date: Wed, 14 Mar 2018 14:03:00 -0700 Subject: [PATCH 36/45] Fix description of parameter value globbing (#4131) --- website/source/docs/concepts/policies.html.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/website/source/docs/concepts/policies.html.md b/website/source/docs/concepts/policies.html.md index 6573db38dcd4..182af4a0401a 100644 --- a/website/source/docs/concepts/policies.html.md +++ b/website/source/docs/concepts/policies.html.md @@ -269,7 +269,7 @@ options are: ``` * If any keys are specified, all non-specified parameters will be denied - unless there the parameter `"*"` is set to an empty array, which will + unless the parameter `"*"` is set to an empty array, which will allow all other parameters to be modified. Parameters with specific values will still be restricted to those values. @@ -338,15 +338,18 @@ Parameter values also support prefix/suffix globbing. Globbing is enabled by prepending or appending or prepending a splat (`*`) to the value: ```ruby -# Allow any parameter as long as the value starts with "foo-*". +# Only allow a parameter named "bar" with a value starting with "foo-*". path "secret/foo" { capabilities = ["create"] allowed_parameters = { - "*" = ["foo-*"] + "bar" = ["foo-*"] } } ``` +Note: the only value that can be used with the `*` parameter is `[]`. + + ### Required Response Wrapping TTLs These parameters can be used to set minimums/maximums on TTLs set by clients From efb7a23498cb67bb09d1a4afb0444149349217b9 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 15 Mar 2018 11:59:50 -0400 Subject: [PATCH 37/45] Make the API docs around ed25519 more clear about what derivation means for this key type --- website/source/api/secret/transit/index.html.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/source/api/secret/transit/index.html.md b/website/source/api/secret/transit/index.html.md index b1bdc9ba3336..ec1af67ecca2 100644 --- a/website/source/api/secret/transit/index.html.md +++ b/website/source/api/secret/transit/index.html.md @@ -54,8 +54,10 @@ values set here cannot be changed after key creation. (symmetric, supports derivation and convergent encryption) - `chacha20-poly1305` – ChaCha20-Poly1305 AEAD (symmetric, supports derivation and convergent encryption) + - `ed25519` – ED25519 (asymmetric, supports derivation). When using + derivation, a sign operation with the same context will derive the same + key and signature; this is a signing analogue to `convergent_encryption`. - `ecdsa-p256` – ECDSA using the P-256 elliptic curve (asymmetric) - - `ed25519` – ED25519 (asymmetric, supports derivation) - `rsa-2048` - RSA with bit size of 2048 (asymmetric) - `rsa-4096` - RSA with bit size of 4096 (asymmetric) From ecb3fe21b7541739a554c538179cde1df9e95c00 Mon Sep 17 00:00:00 2001 From: Brian Nuszkowski Date: Thu, 15 Mar 2018 12:17:02 -0400 Subject: [PATCH 38/45] Add PKCS1v15 as a RSA signature and verification option on the Transit secret engine (#4018) Option to specify the RSA signature type, in specific add support for PKCS1v15 --- builtin/logical/transit/backend_test.go | 14 ++--- builtin/logical/transit/path_sign_verify.go | 57 ++++++++++++++----- .../logical/transit/path_sign_verify_test.go | 12 ++-- helper/keysutil/policy.go | 45 +++++++++++---- .../source/api/secret/transit/index.html.md | 25 ++++++-- 5 files changed, 110 insertions(+), 43 deletions(-) diff --git a/builtin/logical/transit/backend_test.go b/builtin/logical/transit/backend_test.go index 493fea1cc8bb..7e42192a0b79 100644 --- a/builtin/logical/transit/backend_test.go +++ b/builtin/logical/transit/backend_test.go @@ -178,8 +178,8 @@ func testTransit_RSA(t *testing.T, keyType string) { } signReq.Data = map[string]interface{}{ - "input": plaintext, - "algorithm": "invalid", + "input": plaintext, + "hash_algorithm": "invalid", } resp, err = b.HandleRequest(context.Background(), signReq) if err != nil { @@ -190,8 +190,8 @@ func testTransit_RSA(t *testing.T, keyType string) { } signReq.Data = map[string]interface{}{ - "input": plaintext, - "algorithm": "sha2-512", + "input": plaintext, + "hash_algorithm": "sha2-512", } resp, err = b.HandleRequest(context.Background(), signReq) if err != nil || (resp != nil && resp.IsError()) { @@ -212,9 +212,9 @@ func testTransit_RSA(t *testing.T, keyType string) { } verifyReq.Data = map[string]interface{}{ - "input": plaintext, - "signature": signature, - "algorithm": "sha2-512", + "input": plaintext, + "signature": signature, + "hash_algorithm": "sha2-512", } resp, err = b.HandleRequest(context.Background(), verifyReq) if err != nil || (resp != nil && resp.IsError()) { diff --git a/builtin/logical/transit/path_sign_verify.go b/builtin/logical/transit/path_sign_verify.go index cf60ca5853fb..4a0edbf0ffb9 100644 --- a/builtin/logical/transit/path_sign_verify.go +++ b/builtin/logical/transit/path_sign_verify.go @@ -33,7 +33,7 @@ func (b *backend) pathSign() *framework.Path { derivation is enabled; currently only available with ed25519 keys.`, }, - "algorithm": &framework.FieldSchema{ + "hash_algorithm": &framework.FieldSchema{ Type: framework.TypeString, Default: "sha2-256", Description: `Hash algorithm to use (POST body parameter). Valid values are: @@ -47,6 +47,12 @@ Defaults to "sha2-256". Not valid for all key types, including ed25519.`, }, + "algorithm": &framework.FieldSchema{ + Type: framework.TypeString, + Default: "sha2-256", + Description: `Deprecated: use "hash_algorithm" instead.`, + }, + "urlalgorithm": &framework.FieldSchema{ Type: framework.TypeString, Description: `Hash algorithm to use (POST URL parameter)`, @@ -63,6 +69,11 @@ to the min_encryption_version configured on the key.`, Type: framework.TypeBool, Description: `Set to 'true' when the input is already hashed. If the key type is 'rsa-2048' or 'rsa-4096', then the algorithm used to hash the input should be indicated by the 'algorithm' parameter.`, }, + "signature_algorithm": &framework.FieldSchema{ + Type: framework.TypeString, + Description: `The signature algorithm to use for signing. Currently only applies to RSA key types. +Options are 'pss' or 'pkcs1v15'. Defaults to 'pss'`, + }, }, Callbacks: map[logical.Operation]framework.OperationFunc{ @@ -109,7 +120,7 @@ derivation is enabled; currently only available with ed25519 keys.`, Description: `Hash algorithm to use (POST URL parameter)`, }, - "algorithm": &framework.FieldSchema{ + "hash_algorithm": &framework.FieldSchema{ Type: framework.TypeString, Default: "sha2-256", Description: `Hash algorithm to use (POST body parameter). Valid values are: @@ -121,11 +132,21 @@ derivation is enabled; currently only available with ed25519 keys.`, Defaults to "sha2-256". Not valid for all key types.`, }, + "algorithm": &framework.FieldSchema{ + Type: framework.TypeString, + Default: "sha2-256", + Description: `Deprecated: use "hash_algorithm" instead.`, + }, "prehashed": &framework.FieldSchema{ Type: framework.TypeBool, Description: `Set to 'true' when the input is already hashed. If the key type is 'rsa-2048' or 'rsa-4096', then the algorithm used to hash the input should be indicated by the 'algorithm' parameter.`, }, + "signature_algorithm": &framework.FieldSchema{ + Type: framework.TypeString, + Description: `The signature algorithm to use for signature verification. Currently only applies to RSA key types. +Options are 'pss' or 'pkcs1v15'. Defaults to 'pss'`, + }, }, Callbacks: map[logical.Operation]framework.OperationFunc{ @@ -141,11 +162,15 @@ func (b *backend) pathSignWrite(ctx context.Context, req *logical.Request, d *fr name := d.Get("name").(string) ver := d.Get("key_version").(int) inputB64 := d.Get("input").(string) - algorithm := d.Get("urlalgorithm").(string) - if algorithm == "" { - algorithm = d.Get("algorithm").(string) + hashAlgorithm := d.Get("urlalgorithm").(string) + if hashAlgorithm == "" { + hashAlgorithm = d.Get("hash_algorithm").(string) + if hashAlgorithm == "" { + hashAlgorithm = d.Get("algorithm").(string) + } } prehashed := d.Get("prehashed").(bool) + sigAlgorithm := d.Get("signature_algorithm").(string) input, err := base64.StdEncoding.DecodeString(inputB64) if err != nil { @@ -179,7 +204,7 @@ func (b *backend) pathSignWrite(ctx context.Context, req *logical.Request, d *fr if p.Type.HashSignatureInput() && !prehashed { var hf hash.Hash - switch algorithm { + switch hashAlgorithm { case "sha2-224": hf = sha256.New224() case "sha2-256": @@ -189,13 +214,13 @@ func (b *backend) pathSignWrite(ctx context.Context, req *logical.Request, d *fr case "sha2-512": hf = sha512.New() default: - return logical.ErrorResponse(fmt.Sprintf("unsupported algorithm %s", algorithm)), nil + return logical.ErrorResponse(fmt.Sprintf("unsupported hash algorithm %s", hashAlgorithm)), nil } hf.Write(input) input = hf.Sum(nil) } - sig, err := p.Sign(ver, context, input, algorithm) + sig, err := p.Sign(ver, context, input, hashAlgorithm, sigAlgorithm) if err != nil { return nil, err } @@ -234,11 +259,15 @@ func (b *backend) pathVerifyWrite(ctx context.Context, req *logical.Request, d * name := d.Get("name").(string) inputB64 := d.Get("input").(string) - algorithm := d.Get("urlalgorithm").(string) - if algorithm == "" { - algorithm = d.Get("algorithm").(string) + hashAlgorithm := d.Get("urlalgorithm").(string) + if hashAlgorithm == "" { + hashAlgorithm = d.Get("hash_algorithm").(string) + if hashAlgorithm == "" { + hashAlgorithm = d.Get("algorithm").(string) + } } prehashed := d.Get("prehashed").(bool) + sigAlgorithm := d.Get("signature_algorithm").(string) input, err := base64.StdEncoding.DecodeString(inputB64) if err != nil { @@ -272,7 +301,7 @@ func (b *backend) pathVerifyWrite(ctx context.Context, req *logical.Request, d * if p.Type.HashSignatureInput() && !prehashed { var hf hash.Hash - switch algorithm { + switch hashAlgorithm { case "sha2-224": hf = sha256.New224() case "sha2-256": @@ -282,13 +311,13 @@ func (b *backend) pathVerifyWrite(ctx context.Context, req *logical.Request, d * case "sha2-512": hf = sha512.New() default: - return logical.ErrorResponse(fmt.Sprintf("unsupported algorithm %s", algorithm)), nil + return logical.ErrorResponse(fmt.Sprintf("unsupported hash algorithm %s", hashAlgorithm)), nil } hf.Write(input) input = hf.Sum(nil) } - valid, err := p.VerifySignature(context, input, sig, algorithm) + valid, err := p.VerifySignature(context, input, sig, hashAlgorithm, sigAlgorithm) if err != nil { switch err.(type) { case errutil.UserError: diff --git a/builtin/logical/transit/path_sign_verify_test.go b/builtin/logical/transit/path_sign_verify_test.go index c9c9dc673501..e8fc3a7bd228 100644 --- a/builtin/logical/transit/path_sign_verify_test.go +++ b/builtin/logical/transit/path_sign_verify_test.go @@ -158,11 +158,11 @@ func TestTransit_SignVerify_P256(t *testing.T) { verifyRequest(req, false, "/sha2-224", sig) // Reset and test algorithm selection in the data - req.Data["algorithm"] = "sha2-224" + req.Data["hash_algorithm"] = "sha2-224" sig = signRequest(req, false, "") verifyRequest(req, false, "", sig) - req.Data["algorithm"] = "sha2-384" + req.Data["hash_algorithm"] = "sha2-384" sig = signRequest(req, false, "") verifyRequest(req, false, "", sig) @@ -173,18 +173,18 @@ func TestTransit_SignVerify_P256(t *testing.T) { // Test 512 and save sig for later to ensure we can't validate once min // decryption version is set - req.Data["algorithm"] = "sha2-512" + req.Data["hash_algorithm"] = "sha2-512" sig = signRequest(req, false, "") verifyRequest(req, false, "", sig) v1sig := sig // Test bad algorithm - req.Data["algorithm"] = "foobar" + req.Data["hash_algorithm"] = "foobar" signRequest(req, true, "") // Test bad input - req.Data["algorithm"] = "sha2-256" + req.Data["hash_algorithm"] = "sha2-256" req.Data["input"] = "foobar" signRequest(req, true, "") @@ -204,7 +204,7 @@ func TestTransit_SignVerify_P256(t *testing.T) { } req.Data["input"] = "dGhlIHF1aWNrIGJyb3duIGZveA==" - req.Data["algorithm"] = "sha2-256" + req.Data["hash_algorithm"] = "sha2-256" // Make sure signing still works fine sig = signRequest(req, false, "") verifyRequest(req, false, "", sig) diff --git a/helper/keysutil/policy.go b/helper/keysutil/policy.go index 50bc3b868c30..759d083773da 100644 --- a/helper/keysutil/policy.go +++ b/helper/keysutil/policy.go @@ -942,7 +942,7 @@ func (p *Policy) HMACKey(version int) ([]byte, error) { return p.Keys[strconv.Itoa(version)].HMACKey, nil } -func (p *Policy) Sign(ver int, context, input []byte, algorithm string) (*SigningResult, error) { +func (p *Policy) Sign(ver int, context, input []byte, hashAlgorithm, sigAlgorithm string) (*SigningResult, error) { if !p.Type.SigningSupported() { return nil, fmt.Errorf("message signing not supported for key type %v", p.Type) } @@ -1011,7 +1011,7 @@ func (p *Policy) Sign(ver int, context, input []byte, algorithm string) (*Signin key := p.Keys[strconv.Itoa(ver)].RSAKey var algo crypto.Hash - switch algorithm { + switch hashAlgorithm { case "sha2-224": algo = crypto.SHA224 case "sha2-256": @@ -1021,12 +1021,26 @@ func (p *Policy) Sign(ver int, context, input []byte, algorithm string) (*Signin case "sha2-512": algo = crypto.SHA512 default: - return nil, errutil.InternalError{Err: fmt.Sprintf("unsupported algorithm %s", algorithm)} + return nil, errutil.InternalError{Err: fmt.Sprintf("unsupported hash algorithm %s", hashAlgorithm)} } - sig, err = rsa.SignPSS(rand.Reader, key, algo, input, nil) - if err != nil { - return nil, err + if sigAlgorithm == "" { + sigAlgorithm = "pss" + } + + switch sigAlgorithm { + case "pss": + sig, err = rsa.SignPSS(rand.Reader, key, algo, input, nil) + if err != nil { + return nil, err + } + case "pkcs1v15": + sig, err = rsa.SignPKCS1v15(rand.Reader, key, algo, input) + if err != nil { + return nil, err + } + default: + return nil, errutil.InternalError{Err: fmt.Sprintf("unsupported rsa signature algorithm %s", sigAlgorithm)} } default: @@ -1043,7 +1057,7 @@ func (p *Policy) Sign(ver int, context, input []byte, algorithm string) (*Signin return res, nil } -func (p *Policy) VerifySignature(context, input []byte, sig, algorithm string) (bool, error) { +func (p *Policy) VerifySignature(context, input []byte, sig, hashAlgorithm string, sigAlgorithm string) (bool, error) { if !p.Type.SigningSupported() { return false, errutil.UserError{Err: fmt.Sprintf("message verification not supported for key type %v", p.Type)} } @@ -1121,7 +1135,7 @@ func (p *Policy) VerifySignature(context, input []byte, sig, algorithm string) ( key := p.Keys[strconv.Itoa(ver)].RSAKey var algo crypto.Hash - switch algorithm { + switch hashAlgorithm { case "sha2-224": algo = crypto.SHA224 case "sha2-256": @@ -1131,10 +1145,21 @@ func (p *Policy) VerifySignature(context, input []byte, sig, algorithm string) ( case "sha2-512": algo = crypto.SHA512 default: - return false, errutil.InternalError{Err: fmt.Sprintf("unsupported algorithm %s", algorithm)} + return false, errutil.InternalError{Err: fmt.Sprintf("unsupported hash algorithm %s", hashAlgorithm)} + } + + if sigAlgorithm == "" { + sigAlgorithm = "pss" } - err = rsa.VerifyPSS(&key.PublicKey, algo, input, sigBytes, nil) + switch sigAlgorithm { + case "pss": + err = rsa.VerifyPSS(&key.PublicKey, algo, input, sigBytes, nil) + case "pkcs1v15": + err = rsa.VerifyPKCS1v15(&key.PublicKey, algo, input, sigBytes) + default: + return false, errutil.InternalError{Err: fmt.Sprintf("unsupported rsa signature algorithm %s", sigAlgorithm)} + } return err == nil, nil diff --git a/website/source/api/secret/transit/index.html.md b/website/source/api/secret/transit/index.html.md index ec1af67ecca2..b6f11624967d 100644 --- a/website/source/api/secret/transit/index.html.md +++ b/website/source/api/secret/transit/index.html.md @@ -776,7 +776,7 @@ supports signing. | Method | Path | Produces | | :------- | :--------------------------- | :--------------------- | -| `POST` | `/transit/sign/:name(/:algorithm)` | `200 application/json` | +| `POST` | `/transit/sign/:name(/:hash_algorithm)` | `200 application/json` | ### Parameters @@ -787,7 +787,7 @@ supports signing. signing. If not set, uses the latest version. Must be greater than or equal to the key's `min_encryption_version`, if set. -- `algorithm` `(string: "sha2-256")` – Specifies the hash algorithm to use for +- `hash_algorithm` `(string: "sha2-256")` – Specifies the hash algorithm to use for supporting key types (notably, not including `ed25519` which specifies its own hash algorithm). This can also be specified as part of the URL. Currently-supported algorithms are: @@ -805,7 +805,13 @@ supports signing. - `prehashed` `(bool: false)` - Set to `true` when the input is already hashed. If the key type is `rsa-2048` or `rsa-4096`, then the algorithm used - to hash the input should be indicated by the `algorithm` parameter. + to hash the input should be indicated by the `hash_algorithm` parameter. + +- `signature_algorithm` `(string: "pss")` – When using a RSA key, specifies the RSA + signature algorithm to use for signing. Supported signature types are: + + - `pss` + - `pkcs1v15` ### Sample Payload @@ -843,14 +849,14 @@ data. | Method | Path | Produces | | :------- | :--------------------------- | :--------------------- | -| `POST` | `/transit/verify/:name(/:algorithm)` | `200 application/json` | +| `POST` | `/transit/verify/:name(/:hash_algorithm)` | `200 application/json` | ### Parameters - `name` `(string: )` – Specifies the name of the encryption key that was used to generate the signature or HMAC. -- `algorithm` `(string: "sha2-256")` – Specifies the hash algorithm to use. This +- `hash_algorithm` `(string: "sha2-256")` – Specifies the hash algorithm to use. This can also be specified as part of the URL. Currently-supported algorithms are: - `sha2-224` @@ -874,7 +880,14 @@ data. - `prehashed` `(bool: false)` - Set to `true` when the input is already hashed. If the key type is `rsa-2048` or `rsa-4096`, then the algorithm used - to hash the input should be indicated by the `algorithm` parameter. + to hash the input should be indicated by the `hash_algorithm` parameter. + +- `signature_algorithm` `(string: "pss")` – When using a RSA key, specifies the RSA + signature algorithm to use for signature verification. Supported signature types + are: + + - `pss` + - `pkcs1v15` ### Sample Payload From 416dd6f5479e789dff97a64708352ffc0a619c4f Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 15 Mar 2018 12:18:00 -0400 Subject: [PATCH 39/45] changelog++ --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02c63cdeb5cc..e458bbb0c3e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ IMPROVEMENTS: * auth/approle: Allow array input for bound_cidr_list [4078] * auth/aws: Allow using lists in role bind parameters [GH-3907] + * secret/transit: Allow selecting signature algorithm as well as hash + algorithm when signing/verifying [GH-4018] * server: Make sure `tls_disable_client_cert` is actually a true value rather than just set [GH-4049] * storage/gcs: Allow specifying chunk size for transfers, which can reduce From d349f5b0a7f65df091cff60cbf4c865e0374abff Mon Sep 17 00:00:00 2001 From: Joel Thompson Date: Thu, 15 Mar 2018 10:19:28 -0600 Subject: [PATCH 40/45] auth/aws: Allow binding by EC2 instance IDs (#3816) * auth/aws: Allow binding by EC2 instance IDs This allows specifying a list of EC2 instance IDs that are allowed to bind to the role. To keep style formatting with the other bindings, this is still called bound_ec2_instance_id rather than bound_ec2_instance_ids as I intend to convert the other bindings to accept lists as well (where it makes sense) and keeping them with singular names would be the easiest for backwards compatibility. Partially fixes #3797 --- builtin/credential/aws/backend_test.go | 34 +++++++++++++++++---- builtin/credential/aws/path_login.go | 5 ++++ builtin/credential/aws/path_role.go | 36 ++++++++++++++++++----- builtin/credential/aws/path_role_test.go | 2 ++ website/source/api/auth/aws/index.html.md | 5 ++++ 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/builtin/credential/aws/backend_test.go b/builtin/credential/aws/backend_test.go index 2e8f0eb3e9d7..84b667a4a362 100644 --- a/builtin/credential/aws/backend_test.go +++ b/builtin/credential/aws/backend_test.go @@ -1070,6 +1070,11 @@ func TestBackendAcc_LoginWithInstanceIdentityDocAndWhitelistIdentity(t *testing. "nonce": "vault-client-nonce", } + parsedIdentityDoc, err := b.parseIdentityDocument(context.Background(), storage, pkcs7) + if err != nil { + t.Fatal(err) + } + // Perform the login operation with a AMI ID that is not matching // the bound on the role. loginRequest := &logical.Request{ @@ -1081,12 +1086,13 @@ func TestBackendAcc_LoginWithInstanceIdentityDocAndWhitelistIdentity(t *testing. // Place the wrong AMI ID in the role data. data := map[string]interface{}{ - "auth_type": "ec2", - "policies": "root", - "max_ttl": "120s", - "bound_ami_id": []string{"wrong_ami_id", "wrong_ami_id2"}, - "bound_account_id": accountID, - "bound_iam_role_arn": iamARN, + "auth_type": "ec2", + "policies": "root", + "max_ttl": "120s", + "bound_ami_id": []string{"wrong_ami_id", "wrong_ami_id2"}, + "bound_account_id": accountID, + "bound_iam_role_arn": iamARN, + "bound_ec2_instance_id": []string{parsedIdentityDoc.InstanceID, "i-1234567"}, } roleReq := &logical.Request{ @@ -1139,6 +1145,19 @@ func TestBackendAcc_LoginWithInstanceIdentityDocAndWhitelistIdentity(t *testing. // place a correct IAM role ARN data["bound_iam_role_arn"] = []string{"wrong_iam_role_arn_1", iamARN, "wrong_iam_role_arn_2"} + data["bound_ec2_instance_id"] = "i-1234567" + resp, err = b.HandleRequest(context.Background(), roleReq) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("bad: failed to create role: resp:%#v\nerr:%v", resp, err) + } + // Attempt to login and expect a fail because instance ID is wrong + resp, err = b.HandleRequest(context.Background(), loginRequest) + if err != nil || resp == nil || (resp != nil && !resp.IsError()) { + t.Fatalf("bad: expected error response: resp:%#v\nerr:%v", resp, err) + } + + // place a correct EC2 Instance ID + data["bound_ec2_instance_id"] = []string{parsedIdentityDoc.InstanceID, "i-1234567"} resp, err = b.HandleRequest(context.Background(), roleReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: failed to create role: resp:%#v\nerr:%v", resp, err) @@ -1170,6 +1189,9 @@ func TestBackendAcc_LoginWithInstanceIdentityDocAndWhitelistIdentity(t *testing. if instanceID == "" { t.Fatalf("instance ID not present in the response object") } + if instanceID != parsedIdentityDoc.InstanceID { + t.Fatalf("instance ID in response (%q) did not match instance ID from identity document (%q)", instanceID, parsedIdentityDoc.InstanceID) + } _, ok := resp.Auth.Metadata["nonce"] if ok { diff --git a/builtin/credential/aws/path_login.go b/builtin/credential/aws/path_login.go index 4a58f23cb2de..3a59d34e07ae 100644 --- a/builtin/credential/aws/path_login.go +++ b/builtin/credential/aws/path_login.go @@ -384,6 +384,11 @@ func (b *backend) verifyInstanceMeetsRoleRequirements(ctx context.Context, return nil, fmt.Errorf("nil identityDoc") } + // Verify that the instance ID matches one of the ones set by the role + if len(roleEntry.BoundEc2InstanceIDs) > 0 && !strutil.StrListContains(roleEntry.BoundEc2InstanceIDs, *instance.InstanceId) { + return fmt.Errorf("instance ID %q does not belong to the role %q", *instance.InstanceId, roleName), nil + } + // Verify that the AccountID of the instance trying to login matches the // AccountID specified as a constraint on role if len(roleEntry.BoundAccountIDs) > 0 && !strutil.StrListContains(roleEntry.BoundAccountIDs, identityDoc.AccountID) { diff --git a/builtin/credential/aws/path_role.go b/builtin/credential/aws/path_role.go index 2a1d463dc7ec..d1586024d10a 100644 --- a/builtin/credential/aws/path_role.go +++ b/builtin/credential/aws/path_role.go @@ -71,6 +71,13 @@ with an IAM instance profile ARN which has a prefix that matches one of the values specified by this parameter. The value is prefix-matched (as though it were a glob ending in '*'). This is only applicable when auth_type is ec2 or inferred_entity_type is ec2_instance.`, + }, + "bound_ec2_instance_id": { + Type: framework.TypeCommaStringSlice, + Description: `If set, defines a constraint on the EC2 instances to have one of the +given instance IDs. Can be a list or comma-separated string of EC2 instance +IDs. This is only applicable when auth_type is ec2 or inferred_entity_type is +ec2_instance.`, }, "resolve_aws_unique_ids": { Type: framework.TypeBool, @@ -548,6 +555,10 @@ func (b *backend) pathRoleCreateUpdate(ctx context.Context, req *logical.Request roleEntry.BoundIamInstanceProfileARNs = boundIamInstanceProfileARNRaw.([]string) } + if boundEc2InstanceIDRaw, ok := data.GetOk("bound_ec2_instance_id"); ok { + roleEntry.BoundEc2InstanceIDs = boundEc2InstanceIDRaw.([]string) + } + if boundIamPrincipalARNRaw, ok := data.GetOk("bound_iam_principal_arn"); ok { principalARNs := boundIamPrincipalARNRaw.([]string) roleEntry.BoundIamPrincipalARNs = principalARNs @@ -621,56 +632,63 @@ func (b *backend) pathRoleCreateUpdate(ctx context.Context, req *logical.Request if len(roleEntry.BoundAccountIDs) > 0 { if !allowEc2Binds { - return logical.ErrorResponse(fmt.Sprintf("specified bound_account_id but not allowing ec2 auth_type or inferring %s", ec2EntityType)), nil + return logical.ErrorResponse(fmt.Sprintf("specified bound_account_id but not specifying ec2 auth_type or inferring %s", ec2EntityType)), nil } numBinds++ } if len(roleEntry.BoundRegions) > 0 { if roleEntry.AuthType != ec2AuthType { - return logical.ErrorResponse("specified bound_region but not allowing ec2 auth_type"), nil + return logical.ErrorResponse("specified bound_region but not specifying ec2 auth_type"), nil } numBinds++ } if len(roleEntry.BoundAmiIDs) > 0 { if !allowEc2Binds { - return logical.ErrorResponse(fmt.Sprintf("specified bound_ami_id but not allowing ec2 auth_type or inferring %s", ec2EntityType)), nil + return logical.ErrorResponse(fmt.Sprintf("specified bound_ami_id but not specifying ec2 auth_type or inferring %s", ec2EntityType)), nil } numBinds++ } if len(roleEntry.BoundIamInstanceProfileARNs) > 0 { if !allowEc2Binds { - return logical.ErrorResponse(fmt.Sprintf("specified bound_iam_instance_profile_arn but not allowing ec2 auth_type or inferring %s", ec2EntityType)), nil + return logical.ErrorResponse(fmt.Sprintf("specified bound_iam_instance_profile_arn but not specifying ec2 auth_type or inferring %s", ec2EntityType)), nil + } + numBinds++ + } + + if len(roleEntry.BoundEc2InstanceIDs) > 0 { + if !allowEc2Binds { + return logical.ErrorResponse(fmt.Sprintf("specified bound_ec2_instance_id but not specifying ec2 auth_type or inferring %s", ec2EntityType)), nil } numBinds++ } if len(roleEntry.BoundIamRoleARNs) > 0 { if !allowEc2Binds { - return logical.ErrorResponse(fmt.Sprintf("specified bound_iam_role_arn but not allowing ec2 auth_type or inferring %s", ec2EntityType)), nil + return logical.ErrorResponse(fmt.Sprintf("specified bound_iam_role_arn but not specifying ec2 auth_type or inferring %s", ec2EntityType)), nil } numBinds++ } if len(roleEntry.BoundIamPrincipalARNs) > 0 { if roleEntry.AuthType != iamAuthType { - return logical.ErrorResponse("specified bound_iam_principal_arn but not allowing iam auth_type"), nil + return logical.ErrorResponse("specified bound_iam_principal_arn but not specifying iam auth_type"), nil } numBinds++ } if len(roleEntry.BoundVpcIDs) > 0 { if !allowEc2Binds { - return logical.ErrorResponse(fmt.Sprintf("specified bound_vpc_id but not allowing ec2 auth_type or inferring %s", ec2EntityType)), nil + return logical.ErrorResponse(fmt.Sprintf("specified bound_vpc_id but not specifying ec2 auth_type or inferring %s", ec2EntityType)), nil } numBinds++ } if len(roleEntry.BoundSubnetIDs) > 0 { if !allowEc2Binds { - return logical.ErrorResponse(fmt.Sprintf("specified bound_subnet_id but not allowing ec2 auth_type or inferring %s", ec2EntityType)), nil + return logical.ErrorResponse(fmt.Sprintf("specified bound_subnet_id but not specifying ec2 auth_type or inferring %s", ec2EntityType)), nil } numBinds++ } @@ -794,6 +812,7 @@ type awsRoleEntry struct { AuthType string `json:"auth_type" ` BoundAmiIDs []string `json:"bound_ami_id_list"` BoundAccountIDs []string `json:"bound_account_id_list"` + BoundEc2InstanceIDs []string `json:"bound_ec2_instance_id_list"` BoundIamPrincipalARNs []string `json:"bound_iam_principal_arn_list"` BoundIamPrincipalIDs []string `json:"bound_iam_principal_id_list"` BoundIamRoleARNs []string `json:"bound_iam_role_arn_list"` @@ -830,6 +849,7 @@ func (r *awsRoleEntry) ToResponseData() map[string]interface{} { "auth_type": r.AuthType, "bound_ami_id": r.BoundAmiIDs, "bound_account_id": r.BoundAccountIDs, + "bound_ec2_instance_id": r.BoundEc2InstanceIDs, "bound_iam_principal_arn": r.BoundIamPrincipalARNs, "bound_iam_principal_id": r.BoundIamPrincipalIDs, "bound_iam_role_arn": r.BoundIamRoleARNs, diff --git a/builtin/credential/aws/path_role_test.go b/builtin/credential/aws/path_role_test.go index 18f6a3e40aad..efed15e2db62 100644 --- a/builtin/credential/aws/path_role_test.go +++ b/builtin/credential/aws/path_role_test.go @@ -570,6 +570,7 @@ func TestAwsEc2_RoleCrud(t *testing.T) { "bound_iam_instance_profile_arn": "arn:aws:iam::123456789012:instance-profile/MyInstanceProfile", "bound_subnet_id": "testsubnetid", "bound_vpc_id": "testvpcid", + "bound_ec2_instance_id": "i-12345678901234567,i-76543210987654321", "role_tag": "testtag", "resolve_aws_unique_ids": false, "allow_instance_migration": true, @@ -600,6 +601,7 @@ func TestAwsEc2_RoleCrud(t *testing.T) { "bound_ami_id": []string{"testamiid"}, "bound_account_id": []string{"testaccountid"}, "bound_region": []string{"testregion"}, + "bound_ec2_instance_id": []string{"i-12345678901234567", "i-76543210987654321"}, "bound_iam_principal_arn": []string{}, "bound_iam_principal_id": []string{}, "bound_iam_role_arn": []string{"arn:aws:iam::123456789012:role/MyRole"}, diff --git a/website/source/api/auth/aws/index.html.md b/website/source/api/auth/aws/index.html.md index 23a63b1bae56..16e01c3d0df0 100644 --- a/website/source/api/auth/aws/index.html.md +++ b/website/source/api/auth/aws/index.html.md @@ -588,6 +588,10 @@ list in order to satisfy that constraint. prefix-matched (as though it were a glob ending in `*`). This constraint is checked by the ec2 auth method as well as the iam auth method only when inferring an ec2 instance. This is a comma-separated string or a JSON array. +- `bound_ec2_instance_id` `(list: [])` - If set, defines a constraint on the + EC2 instances to have one of these instance IDs. This constraint is checked by + the ec2 auth method as well as the iam auth method only when inferring an ec2 + instance. This is a comma-separated string or a JSON array. - `role_tag` `(string: "")` - If set, enables the role tags for this role. The value set for this field should be the 'key' of the tag on the EC2 instance. The 'value' of the tag should be generated using `role//tag` endpoint. @@ -681,6 +685,7 @@ list in order to satisfy that constraint. ```json { "bound_ami_id": ["ami-fce36987"], + "bound_ec2_instance_id": ["i-12345678901234567"], "role_tag": "", "policies": [ "default", From f51a7dad6597a649cb719dc0af129ec124a42a6e Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 15 Mar 2018 09:24:02 -0700 Subject: [PATCH 41/45] Honor mount-tuned ttl/max ttl for database credential generatoin (#4053) --- builtin/logical/database/path_creds_create.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/builtin/logical/database/path_creds_create.go b/builtin/logical/database/path_creds_create.go index 7f66f9eaab10..aaab1b766639 100644 --- a/builtin/logical/database/path_creds_create.go +++ b/builtin/logical/database/path_creds_create.go @@ -74,9 +74,16 @@ func (b *databaseBackend) pathCredsCreateRead() framework.OperationFunc { } } - ttl := role.DefaultTTL - if ttl == 0 || (role.MaxTTL > 0 && ttl > role.MaxTTL) { - ttl = role.MaxTTL + ttl := b.System().DefaultLeaseTTL() + if role.DefaultTTL != 0 { + ttl = role.DefaultTTL + } + maxTTL := b.System().MaxLeaseTTL() + if role.MaxTTL != 0 && role.MaxTTL < maxTTL { + maxTTL = role.MaxTTL + } + if ttl > maxTTL { + ttl = maxTTL } expiration := time.Now().Add(ttl) From b6d382808b3f1019faae8f595bb44d4a68049eb9 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 15 Mar 2018 12:24:48 -0400 Subject: [PATCH 42/45] changelog++ --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e458bbb0c3e8..ec1baac4d025 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ BUG FIXES: * cli: Improve error messages around `vault auth help` when there is no CLI helper for a particular method [GH-4056] * cli: Fix autocomplete installation when using Fish as the shell [GH-4094] + * secret/database: Properly honor mount-tuned max TTL [GH-4051] * secret/ssh: Return `key_bits` value when reading a role [GH-4098] ## 0.9.5 (February 26th, 2018) From abd90334fadc1ecdce36b3f7d5ad76d0cfb8a96d Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 15 Mar 2018 12:25:29 -0400 Subject: [PATCH 43/45] changelog++ --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec1baac4d025..ad595da7a29c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ IMPROVEMENTS: * auth/approle: Allow array input for bound_cidr_list [4078] * auth/aws: Allow using lists in role bind parameters [GH-3907] + * auth/aws: Allow binding by EC2 instance IDs [GH-3816] * secret/transit: Allow selecting signature algorithm as well as hash algorithm when signing/verifying [GH-4018] * server: Make sure `tls_disable_client_cert` is actually a true value rather From 4d3455f9f7ecabd721e8fa75dada7b30f032ef2e Mon Sep 17 00:00:00 2001 From: Yoko Date: Thu, 15 Mar 2018 17:16:59 -0700 Subject: [PATCH 44/45] Approle diagram (#4132) * Updates requested by the SE team * Added links to AppRole blog and webinar * Updated diagram * Updated diagram --- .../assets/images/vault-approle-workflow2.png | Bin 53956 -> 66767 bytes .../guides/identity/authentication.html.md | 114 +++++++++--------- .../guides/operations/replication.html.md | 2 + 3 files changed, 61 insertions(+), 55 deletions(-) diff --git a/website/source/assets/images/vault-approle-workflow2.png b/website/source/assets/images/vault-approle-workflow2.png index 66f85e4141eabe72a70381d7de5f39ea07ce3c49..4c9135ea47ce1af627cce498ff34600b31b8c5ad 100644 GIT binary patch literal 66767 zcmZ^KWmKEnwl+{`gA@p~SSgYq72I7yaBXREcPUn^xHLd0P@uH97k77eiWMmmBv_%i zYmhJOefGI`eCOO>86$7<&b6kkXU+M{P&F0Vhj>(Y7#J82<>jESF);2;U|?Xz0`8-~ zNthk@fq_AXArF;&^TA+y9(TI>l_2{*Dt=6mIsaMWbD+7^>lZwEJaVJYvU&2pdq_Ov z0S>*QmrsO#U-RI4^A$4c&+~#rxyz}0J1y@4UH~MW_!;(pe9(PTn_~Je-{1XMLn0k;f|vda z{NLX#0o+8~`|pq1@Boi83^1!av;Lb*^t&PYUjKDD>BL%?0icPO{u+`0v6sI$D4l0Z z^nW~hNv8($#QC`y{ol8m4shN1KeS+kgZ)XZ6hlY!^?$1HFR>XVqW?v^caMtv(Rz3+ zQ7HQ#s{MO|po!}LOZkt{dT7S7I_Xm(KEHu~u!LSMd}-Df1*JX#-ckdR#6UmZ!>fDX zrqg||k+{gQtEtOqYg>WCg)w81Q6iw}c<2LJ{9qfg@4e6CZjh(3Y-ds`x)kZEsbCtjaS6p6YUdWVfU@0|_-9F}~ z7+!bq8P^pL--_qIoLHF8k8vCJ~mulV!F|d^c$a0@;ep7jdn?|oToqet zNo&Z_LNs?|>ewFM8Xq4mr@F7B zC7Bq*M49a5-p|3JTRzalMJ0~#m~Wk3XZXbh=@W~=+UV;vJFGA3t~+fDO|&FHaVp^H zDCKNrk4I=uK(jp5+YmZe6oJxFRu|{XVp!rio$QRP9`(hu5;@BTIyIhl;syQ;?m7FW z|J`u3=Q?Gl($ESumK>bu=v5K~7Deh{Yywjzrtwk^#^#RfAdLawd$D#wg) zjf=7~7G_N=?rZ!{XrhAAG3TQjJuXT^jv5Jtm)frV+#?M_YyD0-w5H#IoJ;)<7F-$%zjBE4LmtK^;;#z z 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 UCl@e{5gqVD)_J&>x$%?!0>>4NRsaA1 diff --git a/website/source/guides/identity/authentication.html.md b/website/source/guides/identity/authentication.html.md index e86d84e1079a..dbcbe114bf49 100644 --- a/website/source/guides/identity/authentication.html.md +++ b/website/source/guides/identity/authentication.html.md @@ -10,34 +10,35 @@ description: |- # Authentication Before a client can interact with Vault, it must authenticate against an [**auth -backend**](/docs/auth/index.html) to acquire a token. This token has policies attached so +method**](/docs/auth/index.html) to acquire a token. This token has policies attached so that the behavior of the client can be governed. Since tokens are the core method for authentication within Vault, there is a -**token** auth backend (often refer as **_token store_**). This is a special -auth backend responsible for creating and storing tokens. +**token** auth method (often referred to as **_token store_**). This is a special +auth method responsible for creating and storing tokens. -### Auth Backends +### Auth Methods -Auth backends perform authentication to verify the user or machine-supplied -information. Some of the supported auth backends are targeted towards users +Auth methods perform authentication to verify the user or machine-supplied +information. Some of the supported auth methods are targeted towards users while others are targeted toward machines or apps. For example, -[**LDAP**](/docs/auth/ldap.html) auth backend enables user authentication using +[**LDAP**](/docs/auth/ldap.html) auth method enables user authentication using an existing LDAP server while [**AppRole**](/docs/auth/approle.html) auth -backend is recommended for machines or apps. +method is recommended for machines or apps. The [Getting Started](/intro/getting-started/authentication.html) guide walks you -through how to enable the GitHub auth backend for user authentication. +through how to enable the GitHub auth method for user authentication. This introductory guide focuses on generating tokens for machines or apps by -enabling the [**AppRole**](/docs/auth/approle.html) auth backend. +enabling the [**AppRole**](/docs/auth/approle.html) auth method. ## Reference Material -- [Getting Started](/intro/getting-started/authentication.html) -- [Auth Backends](/docs/auth/index.html) -- [GitHub Auth APIs](/api/auth/github/index.html) +- [AppRole Auth Methods](/docs/auth/approle.html) +- [AppRole Auth Method (API)](/api/auth/approle/index.html) +- [Authenticating Applications with HashiCorp Vault AppRole](https://www.hashicorp.com/blog/authenticating-applications-with-vault-approle) + ## Estimated Time to Complete @@ -48,7 +49,7 @@ enabling the [**AppRole**](/docs/auth/approle.html) auth backend. The end-to-end scenario described in this guide involves two personas: -- **`admin`** with privileged permissions to configure an auth backend +- **`admin`** with privileged permissions to configure an auth method - **`app`** is the consumer of secrets stored in Vault @@ -58,12 +59,12 @@ Think of a scenario where a DevOps team wants to configure Jenkins to read secrets from Vault so that it can inject the secrets to an app's environment variables (e.g. `MYSQL_DB_HOST`) at deployment time. -Instead of hardcoding secrets in each build script as a plaintext, Jenkins +Instead of hardcoding secrets in each build script as plain text, Jenkins retrieves secrets from Vault. As a user, you can authenticate with Vault using your LDAP credentials, and -Vault generates a token. This token has policies granting you to perform -appropriate operations. +Vault generates a token. This token has policies granting you permission to perform +the appropriate operations. How can a Jenkins server programmatically request a token so that it can read secrets from Vault? @@ -71,7 +72,7 @@ secrets from Vault? ## Solution -Enable **AppRole** auth backend so that the Jenkins server can obtain a Vault +Enable **AppRole** auth method so that the Jenkins server can obtain a Vault token with appropriate policies attached. Since each AppRole has attached policies, you can write fine-grained policies limiting which app can access which path. @@ -87,21 +88,21 @@ unsealed](/intro/getting-started/deploy.html). ### Policy requirements --> **NOTE:** For the purpose of this guide, you can use **`root`** token to work +-> **NOTE:** For the purpose of this guide, you can use the **`root`** token to work with Vault. However, it is recommended that root tokens are only used for just enough initial setup or in emergencies. As a best practice, use tokens with -appropriate set of policies based on your role in the organization. +an appropriate set of policies based on your role in the organization. To perform all tasks demonstrated in this guide, your policy must include the following permissions: ```shell -# Mount the AppRole auth backend +# Mount the AppRole auth method path "sys/auth/approle" { capabilities = [ "create", "read", "update", "delete", "sudo" ] } -# Configure the AppRole auth backend +# Configure the AppRole auth method path "sys/auth/approle/*" { capabilities = [ "create", "read", "update", "delete" ] } @@ -133,7 +134,7 @@ to allow machines or apps to acquire a token to interact with Vault. It uses **Role ID** and **Secret ID** for login. The basic workflow is: -![AppRole auth backend workflow](/assets/images/vault-approle-workflow.png) +![AppRole auth method workflow](/assets/images/vault-approle-workflow.png) > For the purpose of introducing the basics of AppRole, this guide walks you > through a very simple scenario involving only two personas (admin and app). @@ -142,24 +143,24 @@ The basic workflow is: In this guide, you are going to perform the following steps: -1. [Enable AppRole auth backend](#step1) -2. [Create a role with policy attached](#step2) -3. [Get Role ID and Secret ID](#step3) -4. [Login with Role ID & Secret ID](#step4) -5. [Read secrets using the AppRole token](#step5) +1. [Enable AppRole auth method](#step1) +1. [Create a role with policy attached](#step2) +1. [Get Role ID and Secret ID](#step3) +1. [Login with Role ID & Secret ID](#step4) +1. [Read secrets using the AppRole token](#step5) Step 1 through 3 need to be performed by an `admin` user. Step 4 and 5 describe the commands that an `app` runs to get a token and read secrets from Vault. -### Step 1: Enable AppRole auth backend +### Step 1: Enable AppRole auth method (**Persona:** admin) -Like many other auth backends, AppRole must be enabled before it can be used. +Like many other auth methods, AppRole must be enabled before it can be used. #### CLI command -Enable `approle` auth backend by executing the following command: +Enable `approle` auth method by executing the following command: ```shell $ vault auth enable approle @@ -167,7 +168,7 @@ $ vault auth enable approle #### API call using cURL -Enable `approle` auth backend by mounting its endpoint at `/sys/auth/approle`: +Enable `approle` auth method by mounting its endpoint at `/sys/auth/approle`: ```shell $ curl --header "X-Vault-Token: " \ @@ -177,7 +178,7 @@ $ curl --header "X-Vault-Token: " \ ``` Where `` is your valid token, and `` holds [configuration -parameters](/api/system/auth.html#mount-auth-backend) of the backend. +parameters](/api/system/auth.html#enable-auth-method) of the method. **Example:** @@ -189,15 +190,15 @@ $ curl --header "X-Vault-Token: ..." \ https://vault.rocks/v1/sys/auth/approle ``` -The above example passes the **type** (`approle`) in the request payload which +The above example passes the **type** (`approle`) in the request payload at the `sys/auth/approle` endpoint. ### Step 2: Create a role with policy attached (**Persona:** admin) -When you enabled AppRole auth backend, it gets mounted at the +When you enabled the AppRole auth method, it gets mounted at the **`/auth/approle`** path. In this example, you are going to create a role for -**`app`** persona (`jenkins` in our scenario). +the **`app`** persona (`jenkins` in our scenario). The scenario in this guide requires the `app` to have the following policy (`jenkins-pol.hcl`): @@ -216,7 +217,7 @@ path "secret/mysql/*" { #### CLI command -Before creating a role, create `jenkins` policy: +Before creating a role, create a `jenkins` policy: ```shell $ vault policy write jenkins jenkins-pol.hcl @@ -287,8 +288,8 @@ Now, you are ready to create a role. **Example:** -The following example creates a role named `jenkins` with `jenkins` policy -attached. (NOTE: This example creates a role operates in [**pull** +The following example creates a role named `jenkins` with a `jenkins` policy +attached. (NOTE: This example creates a role which operates in [**pull** mode](/docs/auth/approle.html).) ```shell @@ -303,7 +304,7 @@ $ curl --header "X-Vault-Token: ..." --request POST \ > `secret_id_num_uses` or `secret_id_ttl` parameter values. Similarly, you can > specify `token_num_uses` and `token_ttl`. You may never want the app token to > expire. In such a case, specify the `period` so that the token generated by -> this AppRole is a periodic token. To learn more about periodic token, refer to +> this AppRole is a periodic token. To learn more about periodic tokens, refer to > the [Tokens and Leases](/guides/identity/lease.html#step4) guide. @@ -418,7 +419,7 @@ $ curl --header "X-Vault-Token:..." \ You can pass [parameters](/api/auth/approle/index.html#generate-new-secret-id) in the request -payload, or invoke the API with empty payload. +payload, or invoke the API with an empty payload. **Example:** @@ -448,7 +449,7 @@ securely. #### CLI command -To login, use `auth/approle/login` endpoint by passing the role ID and secret ID. +To login, use the `auth/approle/login` endpoint by passing the role ID and secret ID. **Example:** @@ -471,7 +472,7 @@ Now you have a **client token** with `default` and `jenkins` policies attached. #### API call using cURL -To login, use `auth/approle/login` endpoint by passing the role ID and secret ID +To login, use the `auth/approle/login` endpoint by passing the role ID and secret ID in the request payload. **Example:** @@ -543,11 +544,11 @@ $ vault read secret/mysql/webapp No value found at secret/mysql/webapp ``` -Since there is no value in the `secret/mysql/webapp`, it returns "no value +Since there is no value at `secret/mysql/webapp`, it returns a "no value found" message. **Optional:** Using the `admin` user's token, you can store some secrets in the -`secret/mysql/webapp` backend. +`secret/mysql/webapp` path. ```shell $ vault write secret/dev/config/mongodb @mysqldb.txt @@ -561,7 +562,7 @@ $ cat mysqldb.txt } ``` -Now, try to read secrets from `secret/mysql/webapp` using `client_token` again. +Now, try to read secrets from `secret/mysql/webapp` using the `client_token` again. This time, it should return the values you just created. @@ -581,10 +582,10 @@ $ curl --header "X-Vault-Token: 3e7dd0ac-8b3e-8f88-bb37-a2890455ca6e" \ } ``` -Since there is no value in the `secret/mysql/webapp`, it returns an empty array. +Since there is no value at `secret/mysql/webapp`, it returns an empty array. **Optional:** Using the **`admin`** user's token, create some secrets in the -`secret/mysql/webapp` backend. +`secret/mysql/webapp` path. ```shell $ curl --header "X-Vault-Token: ..." --request POST --data @mysqldb.txt \ @@ -598,7 +599,7 @@ $ cat mysqldb.text } ``` -Now, try to read secrets from `secret/mysql/webapp` using `client_token` again. +Now, try to read secrets from `secret/mysql/webapp` using the `client_token` again. This time, it should return the values you just created. @@ -607,7 +608,7 @@ This time, it should return the values you just created. The Role ID is equivalent to a username, and Secret ID is the corresponding password. The app needs both to log in with Vault. Naturally, the next question -becomes how to deliver those values to the expecting client. +becomes how to deliver those values to the expected client. A common solution involves **three personas** instead of two: `admin`, `app`, and `trusted entity`. The `trusted entity` delivers the Role ID and Secret ID to the @@ -617,10 +618,10 @@ For example, Terraform as a trusted entity can deliver the Role ID onto the virtual machine. When the app runs on the virtual machine, the Role ID already exists on the virtual machine. -![AppRole auth backend workflow](/assets/images/vault-approle-workflow2.png) +![AppRole auth method workflow](/assets/images/vault-approle-workflow2.png) Secret ID is like a password. To keep the Secret ID confidential, use -[**response wrapping**](/docs/concepts/response-wrapping.html) so that the only +[**response wrapping**](/docs/concepts/response-wrapping.html) so that only the expected client can unwrap the Secret ID. In [Step 3](#step3), you executed the following command to retrieve the Secret @@ -644,7 +645,7 @@ wrapping_token_creation_time: 2018-01-08 21:29:38.826611 -0800 PST wrapping_token_creation_path: auth/approle/role/jenkins/secret-id ``` -Send this `wrapping_token` to the client so that the response can be unwrap and +Send this `wrapping_token` to the client so that the response can be unwrapped and obtain the Secret ID. ```shell @@ -656,7 +657,7 @@ secret_id 575f23e4-01ad-25f7-2661-9c9bdbb1cf81 secret_id_accessor 7d8a40b7-a6fd-a634-579b-b7d673ff86fb ``` -NOTE: To retrieve the Secret ID alone, you can use `jq` as follow: +NOTE: To retrieve the Secret ID alone, you can use `jq` as follows: ```shell $ VAULT_TOKEN=2577044d-cf86-a065-e28f-e2a14ea6eaf7 vault unwrap -format=json | jq -r ".data.secret_id" @@ -667,5 +668,8 @@ b07d7a47-1d0d-741d-20b4-ae0de7c6d964 ## Next steps -To learn more about response wrapping, go to [Cubbyhole Response +Watch the video recording of the [Delivering Secret Zero: Vault AppRole with Terraform and Chef](Docs.google.com/document/d/1CCbAQ-ZEeSpxQqvo40hM0Y5oe3tBCxM1qYrk7_Wyvww/edit?ts=5a865277#heading=h.i52qa2wbjcsk) +webinar which talks about the usage of AppRole with Terraform and Chef as its trusted entities. + +To learn more about response wrapping, go to the [Cubbyhole Response Wrapping](/guides/secret-mgmt/cubbyhole.html) guide. diff --git a/website/source/guides/operations/replication.html.md b/website/source/guides/operations/replication.html.md index d6e9debd33c0..fac32785cf49 100644 --- a/website/source/guides/operations/replication.html.md +++ b/website/source/guides/operations/replication.html.md @@ -8,6 +8,8 @@ description: |- # Replication Setup & Guidance +~> **Enterprise Only:** Vault replication feature is a part of _Vault Enterprise_. + If you're unfamiliar with Vault Replication concepts, please first look at the [general information page](/docs/vault-enterprise/replication/index.html). More details can be found in the From 530d6cac1f4e2695523518ac6eb7044067fc7150 Mon Sep 17 00:00:00 2001 From: Yoko Date: Thu, 15 Mar 2018 18:23:25 -0700 Subject: [PATCH 45/45] updating the AppRole diagram (#4139) Fixing the build error --- .../assets/images/vault-approle-workflow2.png | Bin 66767 -> 85286 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/website/source/assets/images/vault-approle-workflow2.png b/website/source/assets/images/vault-approle-workflow2.png index 4c9135ea47ce1af627cce498ff34600b31b8c5ad..5d8ece655a11d16160bd9426da36d29a11b72798 100644 GIT binary patch literal 85286 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 QC*V(1P)eYJPsi{71H+`y%K!iX literal 66767 zcmZ^KWmKEnwl+{`gA@p~SSgYq72I7yaBXREcPUn^xHLd0P@uH97k77eiWMmmBv_%i zYmhJOefGI`eCOO>86$7<&b6kkXU+M{P&F0Vhj>(Y7#J82<>jESF);2;U|?Xz0`8-~ zNthk@fq_AXArF;&^TA+y9(TI>l_2{*Dt=6mIsaMWbD+7^>lZwEJaVJYvU&2pdq_Ov z0S>*QmrsO#U-RI4^A$4c&+~#rxyz}0J1y@4UH~MW_!;(pe9(PTn_~Je-{1XMLn0k;f|vda z{NLX#0o+8~`|pq1@Boi83^1!av;Lb*^t&PYUjKDD>BL%?0icPO{u+`0v6sI$D4l0Z z^nW~hNv8($#QC`y{ol8m4shN1KeS+kgZ)XZ6hlY!^?$1HFR>XVqW?v^caMtv(Rz3+ zQ7HQ#s{MO|po!}LOZkt{dT7S7I_Xm(KEHu~u!LSMd}-Df1*JX#-ckdR#6UmZ!>fDX zrqg||k+{gQtEtOqYg>WCg)w81Q6iw}c<2LJ{9qfg@4e6CZjh(3Y-ds`x)kZEsbCtjaS6p6YUdWVfU@0|_-9F}~ z7+!bq8P^pL--_qIoLHF8k8vCJ~mulV!F|d^c$a0@;ep7jdn?|oToqet zNo&Z_LNs?|>ewFM8Xq4mr@F7B zC7Bq*M49a5-p|3JTRzalMJ0~#m~Wk3XZXbh=@W~=+UV;vJFGA3t~+fDO|&FHaVp^H zDCKNrk4I=uK(jp5+YmZe6oJxFRu|{XVp!rio$QRP9`(hu5;@BTIyIhl;syQ;?m7FW z|J`u3=Q?Gl($ESumK>bu=v5K~7Deh{Yywjzrtwk^#^#RfAdLawd$D#wg) zjf=7~7G_N=?rZ!{XrhAAG3TQjJuXT^jv5Jtm)frV+#?M_YyD0-w5H#IoJ;)<7F-$%zjBE4LmtK^;;#z z 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